Android窗口管理服务WindowManagerService显示Activity组件的启动窗口(Starting Window)的过程分析
在Android系统中,Activity组件在启动之后,并且在它的窗口显示出来之前,可以显示一个启动窗口。这个启动窗口可以看作是Activity组件的预览窗口,是由WindowManagerService服务统一管理的,即由WindowManagerService服务负责启动和结束。在本文中,我们就详细分析WindowManagerService服务启动和结束Activity组件的启动窗口的过程。
老罗的新浪微博:http://weibo.com/shengyangluo,欢迎关注!
Activity组件的启动窗口是由ActivityManagerService服务来决定是否要显示的。如果需要显示,那么ActivityManagerService服务就会通知WindowManagerService服务来为正在启动的Activity组件显示一个启动窗口,而WindowManagerService服务又是通过窗口管理策略类PhoneWindowManager来创建这个启动窗口的。这个过程如图1所示。

图1 Activity窗口的启动窗品的创建过程
窗口管理策略类PhoneWindowManager创建完成Activity组件的启动窗口之后,就会请求WindowManagerService服务将该启动窗口显示出来。当Activity组件启动完成,并且它的窗口也显示出来的时候,WindowManagerService服务就会结束显示它的启动窗口。
注意,Activity组件的启动窗口是由ActivityManagerService服务来控制是否显示的,也就是说,Android应用程序是无法决定是否要要Activity组件显示启动窗口的。接下来,我们就分别分析Activity组件的启动窗口的显示和结束过程。
一. Activity组件的启动窗口的显示过程
从前面Android应用程序启动过程源代码分析一文可以知道,Activity组件在启动的过程中,会调用ActivityStack类的成员函数startActivityLocked。注意,在调用ActivityStack类的成员函数startActivityLocked的时候,Actvitiy组件还处于启动的过程,即它的窗口尚未显示出来,不过这时候ActivityManagerService服务会检查是否需要为正在启动的Activity组件显示一个启动窗口。如果需要的话,那么ActivityManagerService服务就会请求WindowManagerService服务为正在启动的Activity组件设置一个启动窗口。这个过程如图2所示。

图2 Activity组件的启动窗口的显示过程
这个过程可以分为6个步骤,接下来我们就详细分析每一个步骤。
Step 1. ActivityStack.startActivityLocked
图3 Activity组件的启动窗口的结束过程
这个过程可以分为13个步骤,接下来我们就详细分析每一个步骤。
Step 1. WindowState.performShowLocked
public class WindowManagerService extends IWindowManager.Stub implements Watchdog.Monitor { ...... private void removeWindowInnerLocked(Session session, WindowState win) { win.mRemoved = true; if (mInputMethodTarget == win) { moveInputMethodWindowsIfNeededLocked(false); } ...... mPolicy.removeWindowLw(win); win.removeLocked(); mWindowMap.remove(win.mClient.asBinder()); mWindows.remove(win); mWindowsChanged = true; ...... if (mInputMethodWindow == win) { mInputMethodWindow = null; } else if (win.mAttrs.type == TYPE_INPUT_METHOD_DIALOG) { mInputMethodDialogs.remove(win); } final WindowToken token = win.mToken; final AppWindowToken atoken = win.mAppToken; token.windows.remove(win); if (atoken != null) { atoken.allAppWindows.remove(win); } ...... if (token.windows.size() == 0) { if (!token.explicit) { mTokenMap.remove(token.token); mTokenList.remove(token); } else if (atoken != null) { atoken.firstWindowDrawn = false; } } if (atoken != null) { if (atoken.startingWindow == win) { atoken.startingWindow = null; } else if (atoken.allAppWindows.size() == 0 && atoken.startingData != null) { // If this is the last window and we had requested a starting // transition window, well there is no point now. atoken.startingData = null; } else if (atoken.allAppWindows.size() == 1 && atoken.startingView != null) { // If this is the last window except for a starting transition // window, we need to get rid of the starting transition. ...... Message m = mH.obtainMessage(H.REMOVE_STARTING, atoken); mH.sendMessage(m); } } if (win.mAttrs.type == TYPE_WALLPAPER) { ...... adjustWallpaperWindowsLocked(); } else if ((win.mAttrs.flags&FLAG_SHOW_WALLPAPER) != 0) { adjustWallpaperWindowsLocked(); } if (!mInLayout) { assignLayersLocked(); mLayoutNeeded = true; performLayoutAndPlaceSurfacesLocked(); if (win.mAppToken != null) { win.mAppToken.updateReportedVisibilityLocked(); } } ...... } ...... } 这个函数定义在文件frameworks/base/services/java/com/android/server/WindowManagerService.java中。由于参数win所描述的一个窗口马上就要被删除了,因此,WindowManagerService类的成员函数removeWindowLocked首先就将它的成员变量mRemoved的值设置为true。此外,如果参数win所描述的窗口是系统输入法的目标窗口,那么还需要调用WindowManagerService类的成员函数moveInputMethodWindowsIfNeededLocked来重新移动动系统输入法窗口到其它可能需要输入法的窗口的上面去。
执行完成以上两个操作之后,WindowManagerService类的成员函数removeWindowLocked接下来就可以对参数win所描述的一个窗口进行清理了,包括:
1. 调用WindowManagerService类的成员变量mPolicy的成员函数removeWindowLw来通知窗口管理策略类PhoneWindowManager,参数win所描述的一个窗口被删除了;
2. 调用参数win所指向的一个WindowState对象的成员函数removeLocked来执行自身的清理工作;
3. 将参数win所指向的一个WindowState对象从WindowManagerService类的成员变量mWindowMap和mWindows中删除,即将参数win所描述的一个窗口从窗口堆栈中删除。
执行完成以上三个清理工作之后,窗口堆栈就发生变化了,因此,就需要将WindowManagerService类的成员变量mWindowsChanged的值设置为true。
接下来,WindowManagerService类的成员函数removeWindowLocked还会检查前面被删除的窗口是否是一个输入法窗口或者一个输入法对话框。如果是一个输入法窗口,那么就会将WindowManagerService类的成员变量mInputMethodWindow的值设置为true;如果是一个输入法对话框,那么就会它从WindowManagerService类的成员变量mInputMethodDialogs所描述的一个输入法对话框列表中删除。
WindowManagerService类的成员函数removeWindowLocked的任务还没有完成,它还需要继续从参数win所描述的一个窗口从它的窗口令牌的窗口列表中删除。参数win所描述的一个窗口的窗口令牌保存在它的成员变量mToken中,这个成员变量指向的是一个WindowToken对象。这个WindowToken对象有一个成员变量windows,它指向的是一个ArrayList中。这个ArrayList即为参数win所描述的一个窗口从它的窗口令牌的窗口列表,因此,将参数win所描述的一个窗口从这个窗口列表中删除即可。
如果参数win描述的一个是与Activity组件有关的窗口,那么它的成员变量mAppToken就会指向一个AppWindowToken对象。这个AppWindowToken对象的成员变量allAppWindows所指向的一个ArrayList也会保存有参数win所描述的窗口。因此,这时候也需要将参数win所描述的一个窗口从这个ArrayList中删除。
参数win所描述的一个窗口被删除了以后,与它所对应的窗口令牌的窗口数量就会减少1。如果一个窗口令牌的窗口数量减少1之后变成0,那么就需要考虑将这个窗口令牌从WindowManagerService服务的窗口令牌列表中删除了,即从WindowManagerService类的成员变量mTokenMap和mTokenList中删除,前提是这个窗口令牌不是显式地被增加到WindowManagerService服务中去的,即用来描述这个窗口令牌的一个WindowToken对象的成员变量explicit的值等于false。
另一方面,如果参数win描述的一个是与Activity组件有关的窗口,并且当它被删除之后,与该Activity组件有关的窗口的数量变为0,那么就需要将用来描述该Activity组件的一个AppWindowToken对象的成员变量firstWindowDrawn的值设置为false,以表示该Activity组件的第一个窗口还没有被显示出来,事实上也是表示目前没有窗口与该Activity组件对应。
当参数win描述的一个是与Activity组件有关的窗口的时候,WindowManagerService类的成员函数removeWindowLocked还需要检查该Activity组件是否设置有启动窗口。如果该Activity组件设置有启动窗口的话,那么就需要对它的相应成员变量进行清理。这些检查以及清理工作包括:
1. 如果参数win所描述的窗口即为一个Activity组件的窗口,即它的值等于用来描述与它的宿主Activity组件的一个AppWindowToken对象的成员变量startingWindow的值,那么就需要将AppWindowToken对象的成员变量startingWindow的值设置为null,以便可以表示它所描述的Activity组件的启动窗口已经结束了;
2. 如果删除了参数win所描述的窗口之后,它的宿主Activity组件的窗品数量为0,但是该Activity组件又正在准备显示启动窗口,即用来描述该Activity组件的一个AppWindowToken对象的成员变量startingData的值不等于null,那么就说明这个启动窗口接下来也没有必要显示了,因此,就需要将该AppWindowToken对象的成员变量startingData的值设置为null;
3. 如果删除了参数win所描述的窗口之后,它的宿主Activity组件的窗品数量为1,并且用来描述该Activity组件的一个AppWindowToken对象的成员变量startingView的值不等于null,那么就说明该Activity组件剩下的最后一个窗口即为它的启动窗口,这时候就需要请求WindowManagerService服务结束掉这个启动窗口,因为已经没有必要显示了。
当一个Activity组件剩下的窗口只有一个,并且用来描述该Activity组件的一个AppWindowToken对象的成员变量startingView的值不等于null时,我们是如何知道这个剩下的窗口就是该Activity组件的启动窗口的呢?从前面第一个部分的内容可以知道,当一个Activity组件的启动窗口被创建出来之后,它的顶层视图就会保存在用来描述该Activity组件的一个AppWindowToken对象的成员变量startingView中。因此,如果Activity组件满足上述两个条件,我们就可以判断出它所剩下的一个窗口即为它的启动窗口。注意,在这种情况下,WindowManagerService类的成员函数removeWindowLocked不是马上删除这个启动窗口的,而是通过向WindowManagerService服务所运行在的线程发送一个类型为REMOVE_STARTING的消息,等到该消息被处理时再来删除这个启动窗口。
清理了窗口win的宿主Activity组件的启动窗口相关的数据之后,WindowManagerService类的成员函数removeWindowLocked又继续检查窗口win是否是一个壁纸窗口或者一个显示壁纸的窗口。如果是的话,那么就需要调用WindowManagerService类的成员函数adjustWallpaperWindowsLocked来重新调整系统中的壁纸窗口在窗口堆栈中的位置,即将它们移动到下一个可能需要显示壁纸窗口的其它窗口的下面去。
WindowManagerService类的成员函数removeWindowLocked的最后一个任务是检查WindowManagerService服务当前是否正处于重新布局窗口的状态,即判断WindowManagerService类的成员变量mInLayout的值是否等于true。如果不等于true的话,那么就需要调用WindowManagerService类的成员函数performLayoutAndPlaceSurfacesLocked来重新布局窗口,实际上就是刷新系统的UI。
注意,WindowManagerService类的成员函数removeWindowLocked在重新布局系统中的窗口之前,还需要调用另外一个成员函数assignLayersLocked来重新计算系统中的所有窗口的Z轴位置了。此外,WindowManagerService类的成员函数removeWindowLocked在重新布局了系统中的窗口之后,如果发现前面被删除的窗口win是一个与Activity组件相关的窗口,即它的成员变量mAppToken的值不等于null,那么还会调用这个成员变量所指向的一个AppWindowToken对象的成员函数updateReportedVisibilityLocked来向相应的应用程序进程报告该Activity组件的可见性。
这一步执行完成之后,一个的Activity组件的启动窗口结束掉了。至此,我们就分析完成Activity组件的启动窗口的启动过程和结束过程了。事实上,一个Activity组件在启动的过程中,除了可能需要显示启动窗口之外,还需要与系统当前激活的Activity组件执行一个切换操作,然后才可以将自己的窗口显示出来。在接下来的一篇文章中,我们就将继续分析Activity组件的切换过程,敬请关注!
老罗的新浪微博:http://weibo.com/shengyangluo,欢迎关注!
