窗口的组织方式

在Android系统中,Activity是以堆栈的形式组织在ActivityManagerService服务中的。与Activity类似,Android系统中的窗口也是以堆栈的形式组织在WindowManagerService服务中的,其中,Z轴位置较低的窗口位于Z轴位置较高的窗口的下面。

应用程序进程中的每一个Activity组件在Activity管理服务ActivityManagerService中都对应有一个ActivityRecord对象。Activity管理服务ActivityManagerService中每一个ActivityRecord对象在Window管理服务WindowManagerService中都对应有一个AppWindowToken对象。

此外,在输入法管理服务InputMethodManagerService中,每一个输入法窗口都对应有一个Binder对象,这个Binder对象在Window管理服务WindowManagerService又对应有一个WindowToken对象。

与输入法窗口类似,在壁纸管理服务WallpaperManagerService中,每一个壁纸窗口都对应有一个Binder对象,这个Binder对象在Window管理服务WindowManagerService也对应有一个WindowToken对象。

在Window管理服务WindowManagerService中,无论是AppWindowToken对象,还是WindowToken对象,它们都是用来描述一组有着相同令牌的窗口的,每一个窗口都是通过一个WindowState对象来描述的。例如,一个Activity组件窗口可能有一个启动窗口(Starting Window),还有若干个子窗口,那么这些窗口就会组成一组,并且都是以Activity组件在Window管理服务WindowManagerService中所对应的AppWindowToken对象为令牌的。

从抽象的角度来看,就是在Window管理服务WindowManagerService中,每一个令牌(AppWindowToken或者WindowToken)都是用来描述一组窗口(WindowState)的,并且每一个窗口的子窗口也是与它同属于一个组,即都有着相同的令牌。

上述的窗口组织方式如图所示:

在这里插入图片描述
其中,Activity Stack是在ActivityManagerService服务中创建的,Token List和Window Stack是在WindowManagerService中创建的,而Binder for IM和Binder for WP分别是在InputMethodManagerService服务和WallpaperManagerService服务中创建的,用来描述一个输入法窗口和一个壁纸窗口。

ActivityRecord-J对应于AppWindowToken-J,后者描述的一组窗口是{WindowState-A, WindowState-B, WindowState-B-1},其中, WindowState-B-1是WindowState-B的子窗口。

ActivityRecord-K对应于AppWindowToken-K,后者描述的一组窗口是{WindowState-C, WindowState-C-1, WindowState-D, WindowState-D-1},其中, WindowState-C-1是WindowState-C的子窗口,WindowState-D-1是WindowState-D的子窗口。

ActivityRecord-N对应于AppWindowToken-N,后者描述的一组窗口是{WindowState-E},其中, WindowState-E是系统当前激活的Activity窗口。

Binder for IM对应于WindowToken-I,后者描述的一组窗口是{WindowState-I},其中, WindowState-I是WindowState-E的输入法窗口。

Binder for WP对应于WindowToken-W,后者描述的一组窗口是{WindowState-W},其中, WindowState-W是WindowState-E的壁纸窗口。

Window Stack中的WindowState是按照它们所描述的窗口的Z轴位置从低到高排列的。


一、增加AppWindowToken

一个Activity组件在启动的过程中,ActivityManagerService服务会调用调用WindowManagerService类的成员函数addAppToken来为它增加一个AppWindowToken,如下所示:

public class WindowManagerService extends IWindowManager.Stub    
        implements Watchdog.Monitor {
       
    ......    
    
    /**  
     * Mapping from a token IBinder to a WindowToken object.  
     */    
    final HashMap<IBinder, WindowToken> mTokenMap =    
            new HashMap<IBinder, WindowToken>();    
    
    /**  
     * The same tokens as mTokenMap, stored in a list for efficient iteration  
     * over them.  
     */    
    final ArrayList<WindowToken> mTokenList = new ArrayList<WindowToken>();    
    ......    
    
    /**  
     * Z-ordered (bottom-most first) list of all application tokens, for  
     * controlling the ordering of windows in different applications.  This  
     * contains WindowToken objects.  
     */    
    final ArrayList<AppWindowToken> mAppTokens = new ArrayList<AppWindowToken>();    
    ......    
    
    public void addAppToken(int addPos, IApplicationToken token,    
            int groupId, int requestedOrientation, boolean fullscreen) {
       
        ......    
    
        synchronized(mWindowMap) {
       
            AppWindowToken wtoken = findAppWindowToken(token.asBinder());    
            if (wtoken != null) {
       
                ......    
                return;    
            }    
            wtoken = new AppWindowToken(token);    
            ......    
            mAppTokens.add(addPos, wtoken);    
            ......    
            mTokenMap.put(token.asBinder(), wtoken);    
            mTokenList.add(wtoken);    
    
            ......   
        }    
    }    
    
    ......    
}   

WindowManagerService类有三个成员变量mTokenMap、mTokenList和mAppTokens,它们都是用来描述系统中的窗口的。

  • 成员变量mTokenMap指向的是一个HashMap,它里面保存的是一系列的WindowToken对象,每一个WindowToken对象都是用来描述一个窗口的,并且是以描述这些窗口的一个Binder对象的IBinder接口为键值的。例如,对于Activity组件类型的窗口来说,它们分别是以用来描述它们的一个ActivityRecord对象的IBinder接口保存在成员变量mTokenMap所指向的一个HashMap中的。
  • 成员变量mTokenList指向的是一个ArrayList,它里面保存的也是一系列WindowToken对象,这些WindowToken对象与保存在成员变量mTokenMap所指向的一个HashMap中的WindowToken对象是一样的。成员变量mTokenMap和成员变量mTokenList的区别就在于,前者在给定一个IBinder接口的情况下,可以迅速指出是否存在一个对应的WindowToken对象,而后者可以迅速遍历WindowManagerService服务中的WindowToken对象。
  • 成员变量mAppTokens指向的也是一个ArrayList,不过它里面保存的是一系列AppWindowToken对象,每一个AppWindowToken对象都是用来描述一个Activity组件窗口的,而这些AppWindowToken对象是以它们描述的窗口的Z轴坐标由小到大保存在这个ArrayList中的,这样我们就可以通过这个ArrayList来从上到下或者从下到上地遍历系统中的所有Activity组件窗口。由于这些AppWindowToken对象所描述的Activity组件窗口也是一个窗口,并且AppWindowToken类是从WindowToken继承下来的,因此,这些AppWindowToken对象还会同时被保存在成员变量mTokenMap所指向的一个HashMap和成员变量mTokenList所指向的一个ArrayList中。

理解了WindowManagerService类的这三个成员变量的含义之后,它的成员函数addAppToken的实现就好理解了,其中,参数token指向的便是用来描述正在启动的Activity组件所对应的一个ActivityRecord对象,而参数addPos用来描述该Activity组件在堆栈中的位置,这个位置同时也是接下来要创建的AppWindowToken对象在WindowManagerService类的mTokenList所描述的一个ArrayList中的位置。

WindowManagerService类的成员函数addAppToken首先调用另外一个成员函数findAppWindowToken来在成员变量mTokenMap所描述的一个HashMap检查是否已经存在一个AppWindowToken。如果已经存在的话,那么WindowManagerService类的成员函数addAppToken就什么也不做就返回了,否则的话,就会使用参数token来创建一个AppWindowToken对象,并且会将该AppWindowToken对象分别保存在WindowManagerService类的成员变量mTokenMap、mTokenList和mAppTokens中。


二、删除AppWindowToken

public class WindowManagerService extends IWindowManager.Stub    
        implements Watchdog.Monitor {
       
    ......    
    
    private void removeAppTokensLocked(List<IBinder> tokens) {
     
        // XXX This should be done more efficiently!  
        // (take advantage of the fact that both lists should be  
        // ordered in the same way.)  
        int N = tokens.size();  
        for (int i=0; i<N; i++) {
     
            IBinder token = tokens.get(i);  
            final AppWindowToken wtoken = findAppWindowToken(token);  
            if (!mAppTokens.remove(wtoken)) {
     
                ......  
                i--;  
                N--;  
            }  
        }  
    }  
   
    ......    
}  

WindowManagerService类的成员函数removeAppTokensLocked可以同时删除一组AppWindowToken对象。参数tokens所描述的是一个IBinder接口列表,与这些IBinder接口所对应的AppWindowToken对象就是接下来要删除的。

WindowManagerService类的成员函数removeAppTokensLocked通过一个for循环来依次调用另外一个成员函数findAppWindowToken,以便可以找到保存在列表tokens中的每一个IBinder接口所对应的AppWindowToken对象,然后将该AppWindowToken对象从WindowManagerService类的成员变量mAppTokens所描述的一个ArrayList中删除。

注意,WindowManagerService类的成员函数removeAppTokensLocked是在内部使用的,它只是把一个AppWindowToken对象从成员变量mAppTokens中删除,而没有从另外两个成员变量mTokenMap和mTokenList中删除。


三、移动AppWindowToken至指定位置

public class WindowManagerService extends IWindowManager.Stub    
        implements Watchdog.Monitor {
       
    ......    
    
    public void moveAppToken(int index, IBinder token) {
     
        if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,  
                "moveAppToken()")) {
     
            throw new SecurityException("Requires MANAGE_APP_TOKENS permission");  
        }  
  
        synchronized(mWindowMap) {
     
            ......  
            final AppWindowToken wtoken = findAppWindowToken(token);  
            if (wtoken == null || !mAppTokens.remove(wtoken)) {
     
                ......  
                return;  
            }  
            mAppTokens.add(index, wtoken);  
            ......  
  
            final long origId = Binder.clearCallingIdentity();  
            ......  
            if (tmpRemoveAppWindowsLocked(wtoken)) {
     
                ......  
                reAddAppWindowsLocked(findWindowOffsetLocked(index), wtoken);  
                ......  
                updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES);  
                mLayoutNeeded = true;  
                performLayoutAndPlaceSurfacesLocked();  
            }  
            Binder.restoreCallingIdentity(origId);  
        }  
    }  
   
    ......    
} 

参数token描述的是要移动的AppWindowToken对象所对应的一个IBinder接口,而参数index描述的是该AppWindowToken对象要移动到的位置。注意,移动一个AppWindowToken对象到指定的位置是需要android.Manifest.permission.MANAGE_APP_TOKENS权限的。

WindowManagerService类的成员函数moveAppToken首先找到与参数token所对应的AppWindowToken对象,并且将该AppWindowToken对象从WindowManagerService类的成员变量mAppTokens所描述的一个ArrayList中移除,这样做的目的是为了接下来可以将该AppWindowToken对象移动至该ArrayList中的指定位置上,即参数index所描述的位置上。

注意,上述操作只是将参数token所对应的AppWindowToken对象移动到了WindowManagerService类的成员变量mAppTokens所描述的一个ArrayList的指定位置上,接下来还需要同时将与该AppWindowToken对象所对应的WindowState对象移动至WindowManagerService服务内部的一个WindowState堆栈合适位置上去。

移动对应的WindowState对象的操作同样也是分两步执行的:第一步先调用WindowManagerService类的成员函数tmpRemoveAppWindowsLocked来将这些WindowState对象从原来的WindowState堆栈位置移除;第二步再调用WindowManagerService类的成员函数reAddAppWindowsLocked来将这些WindowState对象插入到WindowState堆栈的合适位置去。

对应的WindowState对象被移动到的合适位置是通过调用WindowManagerService类的成员函数findWindowOffsetLocked来获得的,它的实现如下所示:

public class WindowManagerService extends IWindowManager.Stub    
        implements Watchdog.Monitor {
       
    ......    
  
    /** 
     * Z-ordered (bottom-most first) list of all Window objects. 
     */  
    final ArrayList<WindowState> mWindows = new ArrayList<WindowState>();  
    ......  
    
    private int findWindowOffsetLocked(int tokenPos) {
     
        final int NW = mWindows.size();  
  
        if (tokenPos >= mAppTokens.size()) {
     
            int i = NW;  
            while (i > 0) {
     
                i--;  
                WindowState win = mWindows.get(i);  
                if (win.getAppToken() != null) {
     
                    return i+1;  
                }  
            }  
        }  
  
        while (tokenPos > 0) {
     
            // Find the first app token below the new position that has  
            // a window displayed.  
            final AppWindowToken wtoken = mAppTokens.get(tokenPos-1);  
            ......  
            if (wtoken.sendingToBottom) {
     
                ......  
                tokenPos--;  
                continue;  
            }  
            int i = wtoken.windows.size();  
            while (i > 0) {
     
                i--;  
                WindowState win = wtoken.windows.get(i);  
                int j = win.mChildWindows.size();  
                while (j > 0) {
     
                    j--;  
                    WindowState cwin = win.mChildWindows.get(j);  
                    if (cwin.mSubLayer >= 0) {
     
                        for (int pos=NW-1; pos>=0; pos--) {
     
                            if (mWindows.get(pos) == cwin) {
     
                                ......  
                                return pos+1;  
                            }  
     
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Stardock Groupy 中文版可以将多个应用程序组织到 Windows 桌面上的分组选项卡中!在一个窗口中将多个打开的应用程序或文档分组在一起,将它们显示在单独的选项卡中,以便更轻松,更快速地访问。本站发布的也有类似的工具,例如,WindowTabs 2018 中文版、Clover 中文版等等,它们都是 Windows 选项卡的增强工具。 Windows 窗口选项卡增强工具 Stardock Groupy 中文版Windows 窗口选项卡增强工具 Stardock Groupy 中文版 Stardock Groupy 中文版特色 将应用程序拖放到一起,以在一个通用的选项卡式界面下将它们分组 一起组织多个应用程序和文档,以方便访问 将相关选项卡分组在一起以优化工作流程 将应用程序组保存在一起以备将来使用 类似于浏览器的界面以快速自然的方式管理标签 快速轻松地向现有组添加新标签 将鼠标悬停在选项卡上以预览窗口内容 在资源管理器选项卡之间复制文件。 将文件拖到目标选项卡,暂停以切换,然后进入目标窗口。 自动将同一应用程序的实例分组在一起 选项卡式界面已有很长的历史了,但是想像一下在一个新的应用程序实例中打开每个新文档或网页会是什么样子。牢记这一点,知道仍有一些软件程序不具有选项卡式UI设计,并且其中一个示例是默认的 Windows 文本编辑器 Notepad。 Groupy 汉化版不仅旨在解决此问题,而且提供可靠的生产力工具,它可以将打开的不同窗口合并到选项卡式界面中,从而使工作区保持整洁和井井有条。 将打开的窗口合并到基于选项卡的界面中 Groupy 中文版的使用非常直观。要将两个窗口组合在一起,只需要将一个窗口拖到另一个窗口的标题栏上。将显示一个分组通知,并且一旦释放鼠标按钮,便可以看到生成的选项卡式界面,两个应用程序中的每一个都显示在单独的选项卡中。 为了简化您的工作,Groupy 会检测所有打开的窗口,并允许您单击一下即可将它们添加到当前组中。此外,它识别窗口所属的进程,并提供选项以将属于该特定进程的所有窗口分组。例如,如果您打开了多个 Windows 资源管理器实例,则无论它们指向什么位置,都可以将它们全部合并到一个新的选项卡式界面中,该界面提供了更轻松,更快速的访问。 像在浏览器中一样,本能地管理新窗口中的标签 使用 Groupy,您可以自然方式管理正在运行的应用程序,就像使用浏览器选项卡一样。例如,您可以使用 Win +~ 组合键在选项卡之间快速切换。也可以四处移动标签以将它们重新排列成一个组或关闭标签。 通过 Groupy 的配置设置,您可以选择选项卡的外观,通过颜色或透明度区分活动和非活动选项卡,以及选择允许 Groupy 合并的窗口。 一个有价值的生产力工具,可组合打开的窗口 Groupy 的最大优势在于,它提供了一个更有条理的工作空间,并且无需查找某些应用程序,打开或关闭窗口。实际上,它是一个非常有用且非常酷的应用程序,值得任何日常计算机用户尝试。当然,进一步的增强(例如撤消关闭选项卡或可以分配给更多与选项卡相关的操作的快捷键的可能性)可能使其更加理想。 Groupy 系统要求 Windows 10 x64,Windows 8.1 x64,Windows 7 x64

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值