在前两文中,咱们分析了Activity组件的窗口对象和视图对象的建立过程。Activity组件在其窗口对象和视图对象建立完成以后,就会请求与WindowManagerService创建一个链接,即请求WindowManagerService为其增长一个WindowState对象,用来描述它的窗口状态。在本文中,咱们就详细分析Activity组件与WindowManagerService的链接过程。java
咱们从两方面来看Activity组件与WindowManagerService服务之间的链接。一方面是从Activity组件到WindowManagerService服务的链接,另外一方面是从WindowManagerService服务到Activity组件的链接。从Activity组件到WindowManagerService服务的链接是以Activity组件所在的应用程序进程为单位来进行的。当一个应用程序进程在启动第一个Activity组件的时候,它便会打开一个到WindowManagerService服务的链接,这个链接以应用程序进程从WindowManagerService服务处得到一个实现了IWindowSession接口的Session代理对象来标志。从WindowManagerService服务到Activity组件的链接是以Activity组件为单位来进行的。在应用程序进程这一侧,每个Activity组件都关联一个实现了IWindow接口的W对象,这个W对象在Activity组件的视图对象建立完成以后,就会经过前面所得到一个Session代理对象来传递给WindowManagerService服务,而WindowManagerService服务接收到这个W对象以后,就会在内部建立一个WindowState对象来描述与该W对象所关联的Activity组件的窗口状态,而且之后就经过这个W对象来控制对应的Activity组件的窗口状态。android
上述Activity组件与WindowManagerService服务之间的链接模型如图1所示:windows
图1 Activity组件与WindowManagerService服务之间的链接模型数组
从图1还能够看出,每个Activity组件在ActivityManagerService服务内部,都对应有一个ActivityRecord对象,这个ActivityRecord对象是Activity组件启动的过程当中建立的,用来描述Activity组件的运行状态,这一点能够参考前面Android应用程序启动过程源代码分析一文。这样,每个Activity组件在应用程序进程、WindowManagerService服务和ActivityManagerService服务三者之间就分别一一地创建了链接。在本文中,咱们主要关注Activity组件在应用程序进程和WindowManagerService服务之间以及在WindowManagerService服务和ActivityManagerService服务之间的链接。安全
接下来咱们就经过Session类、W类和WindowState类的实现来简要描述Activity组件与WindowManagerService服务之间的链接,如图2和图3所示:session
图2 W类的实现app
图3 Session类和WindowState类的实现框架
W类实现了IWindow接口,它的类实例是一个Binder本地对象。从前面Android应用程序窗口(Activity)的视图对象(View)的建立过程分析一文能够知道,一个Activity组件在启动的过程当中,会建立一个关联的ViewRoot对象,用来配合WindowManagerService服务来管理该Activity组件的窗口状态。在这个ViewRoot对象内部,有一个类型为W的成员变量mWindow,它是在ViewRoot对象的建立过程当中建立的。ide
ViewRoot类有一个静态成员变量sWindowSession,它指向了一个实现了IWindowSession接口的Session代理对象。当应用程序进程启动第一个Activity组件的时候,它就会请求WindowManagerService服务发送一个创建链接的Binder进程间通讯请求。WindowManagerService服务接收到这个请求以后,就会在内部建立一个类型为Session的Binder本地对象,而且将这个Binder本地对象返回给应用程序进程,后者因而就会获得一个Session代理对象,而且保存在ViewRoot类的静态成员变量sWindowSession中。svg
有了这个Session代理对象以后,应用程序进程就能够在启动Activity组件的时候,调用它的成员函数add来将与该Activity组件所关联的一个W对象传递给WindowManagerService服务,后者因而就会获得一个W代理对象,而且会以这个W代理对象来建立一个WindowState对象,即将这个W代理对象保存在新建立的WindowState对象的成员变量mClient中。这个WindowState对象的其他成员变量的描述能够参考前面Android应用程序窗口(Activity)实现框架简要介绍和学习计划一文的图7,这里再也不详述。
Session类的描述一样能够参考前面Android应用程序窗口(Activity)实现框架简要介绍和学习计划一文,这里咱们主要描述一下它的做用。从图3能够看出,Session类实现了IWindowSession接口,所以,应用程序进程就能够经过保存在ViewRoot类的静态成员变量sWindowSession所描述的一个Session代理对象所实现的IWindowSession接口来与WindowManagerService服务通讯,例如:
1. 在Activity组件的启动过程当中,调用这个IWindowSession接口的成员函数add能够将一个关联的W对象传递到WindowManagerService服务,以便WindowManagerService服务能够为该Activity组件建立一个WindowState对象。
2. 在Activity组件的销毁过程当中,调用这个这个IWindowSession接口的成员函数remove来请求WindowManagerService服务以前为该Activity组件所建立的一个WindowState对象,这一点能够参考前面Android应用程序键盘(Keyboard)消息处理机制分析一文的键盘消息接收通道注销过程分析。
3. 在Activity组件的运行过程当中,调用这个这个IWindowSession接口的成员函数relayout来请求WindowManagerService服务来对该Activity组件的UI进行布局,以便该Activity组件的UI能够正确地显示在屏幕中。
咱们再来看W类的做用。从图2能够看出,W类实现了IWindow接口,所以,WindowManagerService服务就能够经过它在内部所建立的WindowState对象的成员变量mClient来要求运行在应用程序进程这一侧的Activity组件来配合管理窗口的状态,例如:
1. 当一个Activity组件的窗口的大小发生改变后,WindowManagerService服务就会调用这个IWindow接口的成员函数resized来通知该Activity组件,它的大小发生改变了。
2. 当一个Activity组件的窗口的可见性以后,WindowManagerService服务就会调用这个IWindow接口的成员函数dispatchAppVisibility来通知该Activity组件,它的可见性发生改变了。
3. 当一个Activity组件的窗口得到或者失去焦点以后,WindowManagerService服务就会调用这个IWindow接口的成员函数windowFoucusChanged来通知该Activity组件,它的焦点发生改变了。
理解了Activity组件在应用程序进程和WindowManagerService服务之间的链接模型以后,接下来咱们再经过简要分析Activity组件在WindowManagerService服务和ActivityManagerService服务之间的链接。
Activity组件在WindowManagerService服务和ActivityManagerService服务之间的链接是经过一个AppWindowToken对象来描述的。AppWindowToken类的实现如图4所示:
图4 AppWindowToken类的实现
每个Activity组件在启动的时候,ActivityManagerService服务都会内部为该Activity组件建立一个ActivityRecord对象,而且会以这个ActivityRecord对象所实现的一个IApplicationToken接口为参数,请求WindowManagerService服务为该Activity组件建立一个AppWindowToken对象,即将这个IApplicationToken接口保存在新建立的AppWindowToken对象的成员变量appToken中。同时,这个ActivityRecord对象还会传递给它所描述的Activity组件所运行在应用程序进程,因而,应用程序进程就能够在启动完成该Activity组件以后,将这个ActivityRecord对象以及一个对应的W对象传递给WindowManagerService服务,后者接着就会作两件事情:
1. 根据得到的ActivityRecord对象的IApplicationToken接口来找到与之对应的一个AppWindowToken对象;
2. 根据得到的AppWindowToken对象以及前面传递过来的W代理对象来为正在启动的Activity组件建立一个WindowState对象,而且将该AppWindowToken对象保存在新建立的WindowState对象的成员变量mAppToken中。
顺便提一下,AppWindowToken类是从WindowToken类继续下来的。WindowToken类也是用来标志一个窗口的,不过这个窗口类型除了是应用程序窗口,即Activity组件窗口以外,还能够是其它的,例如,输入法窗口或者壁纸窗口类型等,而AppWindowToken类只是用来描述Activity组件窗口。当WindowToken类是用来描述Activity组件窗口的时候,它的成员变量token指向的就是用来描述该Activity组件的一个ActivityRecord对象所实现的一个IBinder接口,而成员变量appWindowToken指向的就是其子类AppWindowToken对象。当另外一方面,当WindowToken类是用来描述非Activity组件窗口的时候,它的成员变量appWindowToken的值就会等于null。这样,咱们就能够经过WindowToken类的成员变量appWindowToken的值来判断一个WindowToken对象是不是用来描述一个Activity组件窗口的,便是否是用来描述一个应用程序窗口的。
上面所描述的Activity组件在ActivityManagerService服务和WindowManagerService服务之间以及应用程序进程和WindowManagerService服务之间的链接模型比较抽象,接下来,咱们再经过三个过程来分析它们彼此之间的链接模型,以下所示:
1. ActivityManagerService服务请求WindowManagerService服务为一个Activity组件建立一个AppWindowToken对象的过程;
2. 应用程序进程请求WindowManagerService服务建立一个Session对象的过程;
3. 应用程序进程请求WindowManagerService服务为一个Activity组件建立一个WindowState对象的过程。
经过这三个过程的分析,咱们就能够对应用程序进程、ActivityManagerService服务和WindowManagerService服务的关系有一个深入的认识了。
一. AppWindowToken对象的建立过程
从前面Android应用程序启动过程源代码分析一文的Step 9能够知道,Activity组件在启动的过程当中,会调用到ActivityStack类的成员函数startActivityLocked,该函数会请求WindowManagerService服务为当前正在启动的Activity组件建立一个AppWindowToken对象。接下来,咱们就从ActivityStack类的成员函数startActivityLocked开始分析一个AppWindowToken对象的建立过程,如图5所示:
图4 AppWindowToken对象的建立过程
这个过程能够分为3步,接下来咱们就详细分析每个步骤。
Step 1. ActivityStack.startActivityLocked
public class ActivityStack { ...... final ActivityManagerService mService; ...... final ArrayList mHistory = new ArrayList(); ...... private final void startActivityLocked(ActivityRecord r, boolean newTask, boolean doResume) { final int NH = mHistory.size(); int addPos = -1; if (!newTask) { // If starting in an existing task, find where that is... boolean startIt = true; for (int i = NH-1; i >= 0; i--) { ActivityRecord p = (ActivityRecord)mHistory.get(i); if (p.finishing) { continue; } if (p.task == r.task) { // Here it is! Now, if this is not yet visible to the // user, then just add it without starting; it will // get started when the user navigates back to it. addPos = i+1; if (!startIt) { mHistory.add(addPos, r); ...... mService.mWindowManager.addAppToken(addPos, r, r.task.taskId, r.info.screenOrientation, r.fullscreen); ...... return; } break; } if (p.fullscreen) { startIt = false; } } } // Place a new activity at top of stack, so it is next to interact // with the user. if (addPos < 0) { addPos = NH; } ...... // Slot the activity into the history stack and proceed mHistory.add(addPos, r); ...... if (NH > 0) { ...... mService.mWindowManager.addAppToken( addPos, r, r.task.taskId, r.info.screenOrientation, r.fullscreen); ..... } else { // If this is the first activity, don't do any fancy animations, // because there is nothing for it to animate on top of. mService.mWindowManager.addAppToken(addPos, r, r.task.taskId, r.info.screenOrientation, r.fullscreen); } ...... if (doResume) { resumeTopActivityLocked(null); } } ...... }
这个函数定义在文件frameworks/base/services/java/com/android/server/am/ActivityStack.java中。
参数r是一个ActivityRecord对象,用来描述的是要启动的Activity组件,参数newTask是一个布尔变量,用来描述要启动的Activity组件是不是在新任务中启动的,参数doResume也是一个布尔变量,用来描述是否须要立刻将Activity组件启动起来。
ActivityStack类的成员变量mService指向的是系统的ActivityManagerService,另一个成员变量mHistory是一个数组列表,用来描述系统的Activity组件堆栈。
当参数newTask的值等于false时,就说明参数r所描述的Activity组件是在已有的一个任务中启动的,所以,这时候ActivityStack类的成员函数startActivityLocked就会从上到下遍历保存成员变量mHistory,找到一个已有的Activity组件,它与参数r所描述的Activity组件是属于同一个任务的,即它们的成员变量task的值相等。找到这样的一个Activity组件以后,若是位于它上面的其它Activity组件的窗口至少有一个全屏的,即变量startIt的值等于true,那么ActivityStack类的成员函数startActivityLocked就只是将参数r所描述的Activity组件加入到成员变量mHistory所描述的一个Activity组件堆栈中,以及调用成员变量mService所描述的ActivityManagerService服务的成员变量mWindowManager所描述的WindowManagerService服务的成员函数addAppToken来为它建立一个AppWindowToken对象,而后就返回了,即不会执行下面的代码来启动参数r所描述的Activity组件。这至关因而延迟到参数r所描述的Activity组件可见时,才将它启动起来。
当参数newTask的值等于true时,就说明参数r所描述的Activity组件是在一个任务中启动的,这时候ActivityStack类的成员函数startActivityLocked就会首先将它添加到成员变量mHistory所描述的一个Activity组件堆栈,接着再判断它是不是系统中第一个启动的Activity组件。若是是系统中第一个启动的Activity组件,那么ActivityStack类的成员函数startActivityLocked就只是简单地调用WindowManagerService服务的成员函数addAppToken来为它建立一个AppWindowToken对象就完事了。若是不是系统系统中第一个启动的Activity组件,那么ActivityStack类的成员函数startActivityLocked除了会调用WindowManagerService服务的成员函数addAppToken来为它建立一个AppWindowToken对象以外,还会为它建立一些启动动画等,咱们忽略这些代码。
从上面的分析就能够看出,不管如何,ActivityStack类的成员函数startActivityLocked都会调用WindowManagerService服务的成员函数addAppToken为正在启动的Activity组件建立一个AppWindowToken对象。建立完成这个AppWindowToken对象以后,若是参数doResume的值等于true,那么ActivityStack类的成员函数startActivityLocked就会继续调用另一个成员函数resumeTopActivityLocked来继续执行启动参数r所描述的一个Activity组件,这一步能够参考前面Android应用程序启动过程源代码分析一文的Step 10。
接下来,咱们就继续分析WindowManagerService类的成员函数addAppToken的实现,以即可以了解WindowManagerService服务是如何为一个Activity组件建立一个AppWindowToken对象的。
Step 2. WindowManagerService.addAppToken
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) { ...... long inputDispatchingTimeoutNanos; try { inputDispatchingTimeoutNanos = token.getKeyDispatchingTimeout() * 1000000L; } catch (RemoteException ex) { ...... inputDispatchingTimeoutNanos = DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS; } synchronized(mWindowMap) { AppWindowToken wtoken = findAppWindowToken(token.asBinder()); if (wtoken != null) { ...... return; } wtoken = new AppWindowToken(token); wtoken.inputDispatchingTimeoutNanos = inputDispatchingTimeoutNanos; wtoken.groupId = groupId; wtoken.appFullscreen = fullscreen; wtoken.requestedOrientation = requestedOrientation; mAppTokens.add(addPos, wtoken); ...... mTokenMap.put(token.asBinder(), wtoken); mTokenList.add(wtoken); // Application tokens start out hidden. wtoken.hidden = true; wtoken.hiddenRequested = true; //dump(); } } ...... }
这个函数定义在文件frameworks/base/services/java/com/android/server/WindowManagerService.java中。
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接口的状况下,能够迅速指出是否存在一个对应的窗口,然后者能够迅速遍历系统中的窗口。
成员变量mAppTokens指向的也是一个ArrayList,不过它里面保存的是一系列AppWindowToken对象,每个AppWindowToken对象都是用来描述一个Activity组件的窗口的,而这些AppWindowToken对象是以它们描述的窗口的Z轴坐标由小到大保存在这个ArrayList中的,这样咱们就能够经过这个ArrayList来从上到下或者从下到上地遍历系统中的全部Activity组件窗口。因为这些AppWindowToken对象所描述的Activity组件窗口也是一个窗口,而且AppWindowToken类是从WindowToken继承下来的,所以,这些AppWindowToken对象还会同时被保存在成员变量mTokenMap所指向的一个HashMap和成员变量mTokenList所指向的一个ArrayList中。
理解了WindowManagerService类的成员变量mTokenMap、mTokenList和mAppTokens的做用以后,WindowManagerService类的成员函数addAppToken的实现就容易理解了。因为参数token描述的是一个Activity组件窗口,所以,函数就会为它建立一个AppWindowToken对象,而且将这个AppWindowToken对象分别保存在 WindowManagerService类的三个成员变量mTokenMap、mTokenList和mAppTokens中。不过在建立对象,首先会检查与参数token所对应的AppWindowToken对象已经存在。若是已经存在,就什么也不作就返回了。注意,参数addPos用来指定参数token描述的是一个Activity组件在系统Activity组件堆栈中的位置,这个位置同时也指定了为该Activity组件在成员变量成员变量mAppTokens所指向的一个ArrayList中的位置。因为保存在系统Activity组件堆栈的Activity组件原本就是按照它们的Z轴坐标从小到大的顺序来排列的,所以,保存在成员变量mAppTokens所指向的一个ArrayList中的AppWindowToken对象也是按照它们的Z轴坐标从小到大的顺序来排列的。
函数在为参数token所描述一个Activity组件窗口建立了一个AppWindowToken对象以后,还会初始化它的一系列成员变量,这些成员变量的含义以下所示:
1. inputDispatchingTimeoutNanos,表示Activity组件窗口收到了一个IO输入事件以后,若是没有在规定的时间内处理完成该事件,那么系统就认为超时。这个超时值能够由Activity组件自己来指定,便可以经过调用一个对应的ActivityRecord对象的成员函数getKeyDispatchingTimeout来得到。假如Activity组件没有指定的话,那么就会使用默认值DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS,即5000 * 1000000纳秒。
2. groupId,表示Activity组件所属的任务的ID。从前面Android应用程序启动过程源代码分析一文能够知道,每个Activity组件都是属于某一个任务的,而每个任务都用来描述一组相关的Activity组件的,这些Activity组件用来完成用户的某一个操做。
3. appFullscreen,表示Activity组件的窗口是不是全屏的。若是一个Activity组件的窗口是全屏的,那么它就会将位于它下面的全部窗口都挡住,这样就能够在渲染系统UI时进行优化,即不用渲染位于全屏窗口如下的其它窗口。
4. requestedOrientation,表示Activity组件所请求的方向。这个方向能够是横的(LANDSCAPE),也能够是竖的(PORTRAIT)。
5. hidden,表示Activity组件是不是处于不可见状态。
6. hiddenRequested,与hidden差很少,也是表示Activity组件是不是处于不可见状态。二者的区别在于,在设置目标Activity组件的可见状态时,若是系统等待执行Activity组件切换操做,那么目标Activity组件的可见状态不会立刻被设置,即它的hidden值不会立刻被设置,而只是设置它的hiddenRequested值,表示它的可见性状态正在等待被设置。等到系统执行完成Activity组件切换操做以后,二者的值就会一致了。
接下来,咱们继续分析一个AppWindowToken对象的建立过程,它AppWindowToken类的构造函数的实现。
Step 3. new AppWindowToken
public class WindowManagerService extends IWindowManager.Stub implements Watchdog.Monitor { ...... class WindowToken { // The actual token. final IBinder token; // The type of window this token is for, as per WindowManager.LayoutParams. final int windowType; // Set if this token was explicitly added by a client, so should // not be removed when all windows are removed. final boolean explicit; ...... // If this is an AppWindowToken, this is non-null. AppWindowToken appWindowToken; ...... WindowToken(IBinder _token, int type, boolean _explicit) { token = _token; windowType = type; explicit = _explicit; } ...... } class AppWindowToken extends WindowToken { // Non-null only for application tokens. final IApplicationToken appToken; ...... AppWindowToken(IApplicationToken _token) { super(_token.asBinder(), WindowManager.LayoutParams.TYPE_APPLICATION, true); appWindowToken = this; appToken = _token; } ...... } ...... }
这个函数定义在文件frameworks/base/services/java/com/android/server/WindowManagerService.java中。
AppWindowToken类的构造函数首先调用父类WindowToken的构造函数来执行父类的初始化工做,而后从父类WindowToken继承下来的成员变量appWindowToken以及本身的成员变量appToken的值。参数_token指向的是一个ActivityRecord对象的IBinder接口,所以,AppWindowToken类的成员变量appToken描述的就是一个ActivityRecord对象。
WindowToken类的构造函数用来初始化token、windowType和explicit这三个成员变量。在咱们这个场景中,成员变量token指向的也是一个ActivityRecord对象的IBinder接口,用来标志一个Activity组件的窗口,成员变量windowType用来描述窗口的类型,它的值等于WindowManager.LayoutParams.TYPE_APPLICATION,表示这是一个Activity组件窗口,成员变量explicit用来表示窗口是不是由应用程序进程请求添加的。
注意,当一个WindowToken对象的成员变量appWindowToken的值不等于null时,就表示它实际描述的是Activity组件窗口,而且这个成员变量指向的就是与该Activity组件所关联的一个AppWindowToken对象。
至此,咱们就分析完成一个AppWindowToken对象的建立过程了,经过这个过程咱们就能够知道,每个Activity组件,在ActivityManagerService服务内部都有一个对应的ActivityRecord对象,而且在WindowManagerService服务内部关联有一个AppWindowToken对象。
二. Session对象的建立过程
应用程序进程在启动第一个Activity组件的时候,就会请求与WindowManagerService服务创建一个链接,以即可以配合WindowManagerService服务来管理系统中的全部窗口。具体来讲,就是应用程序进程在为它里面启动的第一个Activity组件的视图对象建立一个关联的ViewRoot对象的时候,就会向WindowManagerService服务请求返回一个类型为Session的Binder本地对象,这样应用程序进程就能够得到一个类型为Session的Binder代理对象,之后就能够经过这个Binder代理对象来和WindowManagerService服务进行通讯了。
从前面Android应用程序窗口(Activity)的视图对象(View)的建立过程分析一文能够知道,应用程序进程在为它里面启动的Activity组件的视图对象建立关联ViewRoot对象是经过调用WindowManagerImpl类的成员函数addView来实现的,所以,接下来咱们就从WindowManagerImpl类的成员函数addView开始分析应用程序进程与WindowManagerService服务的链接过程,即一个Session对象的建立过程,如图5所示:
图5 Session对象的建立过程
这个过程能够分为5个步骤,接下来咱们就详细分析每个步骤。
Step 1. WindowManagerImpl.addView
public class WindowManagerImpl implements WindowManager { ...... private void addView(View view, ViewGroup.LayoutParams params, boolean nest) { ...... ViewRoot root; ...... synchronized (this) { ...... root = new ViewRoot(view.getContext()); ...... } // do this last because it fires off messages to start doing things root.setView(view, wparams, panelParentView); } ...... }
这个函数定义在文件frameworks/base/core/java/android/view/WindowManagerImpl.java中。
这里的参数view即为正在启动的Activity组件的视图对象,WindowManagerImpl类的成员函数addView的目标就是为它建立一个ViewRoot对象。这里咱们只关注这个ViewRoot对象的建立过程,即ViewRoot类的构造函数的实现,WindowManagerImpl类的成员函数addView的详细实现能够参考前面Android应用程序窗口(Activity)的视图对象(View)的建立过程分析一文的Step 12。
Step 2. new ViewRoot
public final class ViewRoot extends Handler implements ViewParent, View.AttachInfo.Callbacks { ...... public ViewRoot(Context context) { super(); ...... // Initialize the statics when this class is first instantiated. This is // done here instead of in the static block because Zygote does not // allow the spawning of threads. getWindowSession(context.getMainLooper()); ...... } ...... }
这个函数定义在文件frameworks/base/core/java/android/view/ViewRoot.java文件中。
ViewRoot类的构造函数是经过调用静态成员函数getWindowSession来请求WindowManagerService服务为应用程序进程建立一个返回一个类型为Session的Binder本地对象的,所以,接下来咱们就继续分析ViewRoot类的静态成员函数getWindowSession的实现。
Step 3. ViewRoot.getWindowSession
public final class ViewRoot extends Handler implements ViewParent, View.AttachInfo.Callbacks { ...... static IWindowSession sWindowSession; static final Object mStaticInit = new Object(); static boolean mInitialized = false; ...... public static IWindowSession getWindowSession(Looper mainLooper) { synchronized (mStaticInit) { if (!mInitialized) { try { InputMethodManager imm = InputMethodManager.getInstance(mainLooper); sWindowSession = IWindowManager.Stub.asInterface( ServiceManager.getService("window")) .openSession(imm.getClient(), imm.getInputContext()); mInitialized = true; } catch (RemoteException e) { } } return sWindowSession; } } ...... }
这个函数定义在文件frameworks/base/core/java/android/view/ViewRoot.java文件中。
ViewRoot类的静态成员函数getWindowSession首先得到得到应用程序所使用的输入法管理器,接着再得到系统中的WindowManagerService服务的一个Binder代理对象。有了这个Binder代理对象以后,就能够调用它的成员函数openSession来请求WindowManagerService服务返回一个类型为Session的Binder本地对象。这个Binder本地对象返回来以后,就变成了一个类型为Session的Binder代理代象,即一个实现了IWindowSession接口的Binder代理代象,而且保存在ViewRoot类的静态成员变量sWindowSession中。在请求WindowManagerService服务返回一个类型为Session的Binder本地对象的时候,应用程序进程传递给WindowManagerService服务的参数有两个,一个是实现IInputMethodClient接口的输入法客户端对象,另一个是实现了IInputContext接口的一个输入法上下文对象,它们分别是经过调用前面所得到的一个输入法管理器的成员函数getClient和getInputContext来得到的。
从这里就能够看出,只有当ViewRoot类的静态成员函数getWindowSession第一次被调用的时候,应用程序进程才会请求WindowManagerService服务返回一个类型为Session的Binder本地对象。又因为ViewRoot类的静态成员函数getWindowSession第一次被调用的时候,正好是处于应用程序进程中的第一个Activity组件启动的过程当中,所以,应用程序进程是在启动第一个Activity组件的时候,才会请求与WindowManagerService服务创建链接。一旦链接创建起来以后,ViewRoot类的静态成员变量mInitialized的值就会等于true,而且另一个静态成员变量sWindowSession的值不等于null。同时,ViewRoot类的静态成员函数getWindowSession是线程安全的,这样就能够避免多个线程同时调用它来重复请求WindowManagerService服务为当前应用程序进程建立链接。
接下来,咱们就继续分析求WindowManagerService类的成员函数openSession的实现,以即可以了解一个Session对象的建立过程。
Step 4. indowManagerService.openSession
public class WindowManagerService extends IWindowManager.Stub implements Watchdog.Monitor { ...... public IWindowSession openSession(IInputMethodClient client, IInputContext inputContext) { if (client == null) throw new IllegalArgumentException("null client"); if (inputContext == null) throw new IllegalArgumentException("null inputContext"); Session session = new Session(client, inputContext); return session; } ...... }
这个函数定义在文件frameworks/base/services/java/com/android/server/WindowManagerService.java中。
WindowManagerService类的成员函数openSession的实现很简单,它以应用程序进程传递过来的一个输入法客户端对象和一个输入法上下文对象来参数,来建立一个类型为Session的Binder本地对象,而且将这个类型为Session的Binder本地对象返回给应用程序进程。
接下来咱们就继续分析Session类的构造函数的实现,以即可以了解一个Session对象的建立过程。
Step 5. new Session
public class WindowManagerService extends IWindowManager.Stub implements Watchdog.Monitor { ...... final boolean mHaveInputMethods; ...... IInputMethodManager mInputMethodManager; ...... private final class Session extends IWindowSession.Stub implements IBinder.DeathRecipient { final IInputMethodClient mClient; final IInputContext mInputContext; ...... public Session(IInputMethodClient client, IInputContext inputContext) { mClient = client; mInputContext = inputContext; ...... synchronized (mWindowMap) { if (mInputMethodManager == null && mHaveInputMethods) { IBinder b = ServiceManager.getService( Context.INPUT_METHOD_SERVICE); mInputMethodManager = IInputMethodManager.Stub.asInterface(b); } } long ident = Binder.clearCallingIdentity(); try { // Note: it is safe to call in to the input method manager // here because we are not holding our lock. if (mInputMethodManager != null) { mInputMethodManager.addClient(client, inputContext, mUid, mPid); } else { client.setUsingInputMethod(false); } client.asBinder().linkToDeath(this, 0); } catch (RemoteException e) { ...... } finally { Binder.restoreCallingIdentity(ident); } } ...... } ...... }
这个函数定义在文件frameworks/base/services/java/com/android/server/WindowManagerService.java中。
Session类有两个成员变量mClient和mInputContext,分别用来保存在从应用程序进程传递过来的一个输入法客户端对象和一个输入法上下文对象,它们是在Session类的构造函数中初始化的。
Session类的构造函数还会检查WindowManagerService服务是否须要得到系统中的输入法管理器服务,即检查WindowManagerService类的成员变量mHaveInputMethods的值是否等于true。若是这个值等于true,而且WindowManagerService服务尚未得到系统中的输入法管理器服务,即WindowManagerService类的成员变量mInputMethodManager的值等于null,那么Session类的构造函数就会首先得到这个输入法管理器服务,而且保存在WindowManagerService类的成员变量mInputMethodManager中。
得到了系统中的输入法管理器服务以后,Session类的构造函数就能够调用它的成员函数addClient来为正在请求与WindowManagerService服务创建链接的应用程序进程增长它所使用的输入法客户端对象和输入法上下文对象了。
至此,咱们就分析完成一个Session对象的建立过程了,经过这个过程咱们就能够知道,每个应用程序进程在WindowManagerService服务内部都有一个类型为Session的Binder本地对象,用来它与WindowManagerService服务之间的链接,而有了这个链接以后,WindowManagerService服务就能够请求应用进程配合管理系统中的应用程序窗口了。
三. WindowState对象的建立过程
在Android系统中,WindowManagerService服务负责统一管理系统中的全部窗口,所以,当运行在应用程序进程这一侧的Activity组件在启动完成以后,须要与WindowManagerService服务创建一个链接,以便WindowManagerService服务能够管理它的窗口。具体来讲,就是应用程序进程将一个Activitty组件的视图对象设置到与它所关联的一个ViewRoot对象的内部的时候,就会将一个实现了IWindow接口的Binder本地对象传递WindowManagerService服务。这个实现了IWindow接口的Binder本地对象惟一地标识了一个Activity组件,WindowManagerService服务接收到了这个Binder本地对象以后,就会将它保存在一个新建立的WindowState对象的内部,这样WindowManagerService服务之后就能够经过它来和Activity组件通讯,以即可以要求Activity组件配合来管理系系统中的全部窗口。
从前面Android应用程序窗口(Activity)的视图对象(View)的建立过程分析一文能够知道,应用程序进程将一个Activity组件的视图对象设置到与它所关联的一个ViewRoot对象的内部是经过调用ViewRoot类的成员函数setView来实现的,所以,接下来咱们就从ViewRoot类的成员函数setView开始分析Activity组件与WindowManagerService服务的链接过程,即一个WindowState对象的建立过程,如图6所示:
图6 WindowState对象的建立过程
这个过程能够分为7个步骤,接下来咱们就详细分析每个步骤。
Step 1. ViewRoot.setView
public final class ViewRoot extends Handler implements ViewParent, View.AttachInfo.Callbacks { ...... static IWindowSession sWindowSession; ...... final W mWindow; View mView; ...... public ViewRoot(Context context) { super(); ...... mWindow = new W(this, context); ...... } ...... public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) { synchronized (this) { if (mView == null) { mView = view; ...... try { res = sWindowSession.add(mWindow, mWindowAttributes, getHostVisibility(), mAttachInfo.mContentInsets, mInputChannel); } catch (RemoteException e) { ...... } finally { ...... } ...... } ...... } } ...... }
这个函数定义在文件frameworks/base/core/java/android/view/ViewRoot.java文件中。
这里的参数view即为正在启动的Activity组件的视图对象,ViewRoot类的成员函数setView会将它保存成员变量mView中,这样就能够将一个Activity组件的视图对象和一个ViewRoot对象关联起来。ViewRoot类的成员函数setView接下来还会调用静态成员变量sWindowSession所描述的一个实现了IWindowSession接口的Binder代理对象的成员函数add来请求WindowManagerService服务为正在启动的Activity组件建立一个WindowState对象。接下来咱们就主要关注WindowState对象的建立过程,ViewRoot类的成员函数setView的详细实现能够参考前面Android应用程序窗口(Activity)的视图对象(View)的建立过程分析一文的Step 13。
注意,ViewRoot类的成员函数setView在请求WindowManagerService服务为正在启动的Activity组件建立一个WindowState对象的时候,会传递一个类型为W的Binder本地对象给WindowManagerService服务。这个类型为W的Binder本地对象实现了IWindow接口,保存在ViewRoot类的成员变量mWindow中,它是在ViewRoot类的构造函数中建立的,之后WindowManagerService服务就会经过它来和Activity组件通讯。
从前面第二部的内容能够知道,ViewRoot类的静态成员变量sWindowSession所指向的一个Binder代理对象引用的是运行在WindowManagerService服务这一侧的一个Session对象,所以,接下来咱们就继续分析Session类的成员函数add的实现,以即可以了解一个WindowState对象的建立过程。
Step 2. Session.add
public class WindowManagerService extends IWindowManager.Stub implements Watchdog.Monitor { ...... private final class Session extends IWindowSession.Stub implements IBinder.DeathRecipient { ...... public int add(IWindow window, WindowManager.LayoutParams attrs, int viewVisibility, Rect outContentInsets, InputChannel outInputChannel) { return addWindow(this, window, attrs, viewVisibility, outContentInsets, outInputChannel); } ...... } ...... }
这个函数定义在文件frameworks/base/services/java/com/android/server/WindowManagerService.java中。
Session类的成员函数add的实现很简单,它只是调用了外部类WindowManagerService的成员函数addWindow来进一步为正在启动的Activity组件建立一个WindowState对象。
Step 3. WindowManagerService.addWindow
这个函数定义在文件frameworks/base/services/java/com/android/server/WindowManagerService.java中,它的实现比较长,咱们分段来阅读:
public class WindowManagerService extends IWindowManager.Stub implements Watchdog.Monitor { ...... public int addWindow(Session session, IWindow client, WindowManager.LayoutParams attrs, int viewVisibility, Rect outContentInsets, InputChannel outInputChannel) { ...... WindowState attachedWindow = null; WindowState win = null; synchronized(mWindowMap) { ...... if (mWindowMap.containsKey(client.asBinder())) { ...... return WindowManagerImpl.ADD_DUPLICATE_ADD; }
这段代码首先在WindowManagerService类的成员变量mWindowMap所描述的一个HashMap中检查是否存在一个与参数client所对应的WindowState对象,若是已经存在,那么就说明WindowManagerService服务已经为它建立过一个WindowState对象了,所以,这里就不会继续往前执行,而是直接返回一个错误码WindowManagerImpl.ADD_DUPLICATE_ADD。
咱们继续往前看代码:
if (attrs.type >= FIRST_SUB_WINDOW && attrs.type <= LAST_SUB_WINDOW) { attachedWindow = windowForClientLocked(null, attrs.token, false); if (attachedWindow == null) { ...... return WindowManagerImpl.ADD_BAD_SUBWINDOW_TOKEN; } if (attachedWindow.mAttrs.type >= FIRST_SUB_WINDOW && attachedWindow.mAttrs.type <= LAST_SUB_WINDOW) { ...... return WindowManagerImpl.ADD_BAD_SUBWINDOW_TOKEN; } }
参数attrs指向的是一个WindowManager.LayoutParams对象,用来描述正在启动的Activity组件的UI布局,当它的成员变量type的值大于等于FIRST_SUB_WINDOW而且小于等于LAST_SUB_WINDOW的时候,就说明如今要增长的是一个子窗口。在这种状况下,就必需要指定一个父窗口,而这个父窗口是经过数attrs指向的是一个WindowManager.LayoutParams对象的成员变量token来指定的,所以,这段代码就会调用WindowManagerService类的另一个成员函数windowForClientLocked来得到用来描述这个父窗口的一个WindowState对象,而且保存在变量attachedWindow。
若是获得变量attachedWindow的值等于null,那么就说明父窗口不存在,这是不容许的,所以,函数就不会继续向前执行,而是直接返回一个错误码WindowManagerImpl.ADD_BAD_SUBWINDOW_TOKEN。另外一方面,若是变量attachedWindow的值不等于null,可是它的成员变量mAttrs所指向的一个WindowManager.LayoutParams对象的成员变量type的值也是大于等于FIRST_SUB_WINDOW而且小于等于LAST_SUB_WINDOW,那么也说明找到的父窗口也是一个子窗口,这种状况也是不容许的,所以,函数就不会继续向前执行,而是直接返回一个错误码WindowManagerImpl.ADD_BAD_SUBWINDOW_TOKEN。
咱们继续往前看代码:
boolean addToken = false; WindowToken token = mTokenMap.get(attrs.token); if (token == null) { if (attrs.type >= FIRST_APPLICATION_WINDOW && attrs.type <= LAST_APPLICATION_WINDOW) { ...... return WindowManagerImpl.ADD_BAD_APP_TOKEN; } if (attrs.type == TYPE_INPUT_METHOD) { ...... return WindowManagerImpl.ADD_BAD_APP_TOKEN; } if (attrs.type == TYPE_WALLPAPER) { ...... return WindowManagerImpl.ADD_BAD_APP_TOKEN; } token = new WindowToken(attrs.token, -1, false); addToken = true; } else if (attrs.type >= FIRST_APPLICATION_WINDOW && attrs.type <= LAST_APPLICATION_WINDOW) { AppWindowToken atoken = token.appWindowToken; if (atoken == null) { ...... return WindowManagerImpl.ADD_NOT_APP_TOKEN; } else if (atoken.removed) { ...... return WindowManagerImpl.ADD_APP_EXITING; } if (attrs.type == TYPE_APPLICATION_STARTING && atoken.firstWindowDrawn) { // No need for this guy! ...... return WindowManagerImpl.ADD_STARTING_NOT_NEEDED; } } else if (attrs.type == TYPE_INPUT_METHOD) { if (token.windowType != TYPE_INPUT_METHOD) { ...... return WindowManagerImpl.ADD_BAD_APP_TOKEN; } } else if (attrs.type == TYPE_WALLPAPER) { if (token.windowType != TYPE_WALLPAPER) { ...... return WindowManagerImpl.ADD_BAD_APP_TOKEN; } }
这段代码首先在WindowManagerService类的成员变量mTokenMap所描述的一个HashMap中检查WindowManagerService服务是否已经为正在增长的窗口建立过一个WindowToken对象了。
若是尚未建立过WindowToken对,即变量token的值等于null,那么这段代码就会进一步检查正在增长的窗口的类型。若是正在增长的窗口是属于应用程序窗口(FIRST_APPLICATION_WINDOW~LAST_APPLICATION_WINDOW,即Activity组件窗口),或者输入法窗口(TYPE_INPUT_METHOD),或者壁纸窗口(TYPE_WALLPAPER),那么这三种状况都是不容许的,所以,函数就不会继续向前执行,而是直接返回一个错误码WindowManagerImpl.ADD_BAD_APP_TOKEN。例如,对于应用程序窗口来讲,与它所对应的Activity组件是在以前就已经由ActivityManagerService服务请求WindowManagerService服务建立过一个AppWindowToken对象了的(AppWindowToken对象也是一个WindowToken对象,由于AppWindowToken类继承了WindowToken类),这个过程能够参考前面第一部分的内容。若是不是上述三种状况,那么这段代码就会为正在增长的窗口建立一个WindowToken对象,而且保存在变量token中。
若是已经建立过WindowToken对象,即变量token的值不等于null,那么就说明正在增长的窗口或者是应用程序窗口,或者是输入法窗口,或者是壁纸窗口。下面咱们就为三种状况来讨论。
若是是应用程序窗口,从前面第一部分的内容能够知道,WindowToken对象token的成员变量appWindowToken的值必须不能等于null,而且指向的是一个AppWindowToken对象。所以,当WindowToken对象token的成员变量appWindowToken的值等于null的时候,函数就不会继续向前执行,而是直接返回一个错误码WindowManagerImpl.ADD_NOT_APP_TOKEN。另外一方面,虽然WindowToken对象token的成员变量appWindowToken的值不等于null,可是它所指向的一个AppWindowToken对象的成员变量removed的值等于true时,那么就表示对应的Activity组件已经被移除,在这种状况下,函数也不会继续向前执行,而是直接返回一个错误码WindowManagerImpl.ADD_APP_EXITING。还有一种特殊的应用程序窗口,它的类型为TYPE_APPLICATION_STARTING。这种类型的窗口称为起始窗口,它是在一个Activity组件的窗口显示出来以前就显示的。所以,若是当前正在增长的是一个超始窗口,而且它所附属的应用程序窗口,即变量atoken所描述的应用程序窗口,已经显示出来了,即变量atoken所指向的一个AppWindowToken对象的成员变量firstWindowDrawn的值等于true,那么函数也不会继续向前执行,而是直接返回一个错误码WindowManagerImpl.ADD_STARTING_NOT_NEEDED。
若是是输入法窗口,可是参数attrs所描述的一个WindowManager.LayoutParams对象的成员变量windowType的值不等于TYPE_INPUT_METHOD,那么指定的窗口类型就是不匹配的。在这种状况下,函数就不会继续向前执行,而是直接返回一个错误码WindowManagerImpl.ADD_BAD_APP_TOKEN。
若是是壁纸窗口,可是参数attrs所描述的一个WindowManager.LayoutParams对象的成员变量windowType的值不等于TYPE_WALLPAPER,那么指定的窗口类型就是不匹配的。在这种状况下,函数也不会继续向前执行,而是直接返回一个错误码WindowManagerImpl.ADD_BAD_APP_TOKEN。
咱们继续往前看代码:
win = new WindowState(session, client, token, attachedWindow, attrs, viewVisibility); ...... mPolicy.adjustWindowParamsLw(win.mAttrs); res = mPolicy.prepareAddWindowLw(win, attrs); if (res != WindowManagerImpl.ADD_OKAY) { return res; }
经过上面的合法性检查以后,这里就能够为正在增长的窗口建立一个WindowState对象了。
WindowManagerService类的成员变量mPolicy指向的是一个实现了WindowManagerPolicy接口的窗口管理策略器。在Phone平台中,这个窗口管理策略器是由com.android.internal.policy.impl.PhoneWindowManager来实现的,它负责对系统中的窗口实现制定一些规则。这里主要是调用窗口管理策略器的成员函数adjustWindowParamsLw来调整当前正在增长的窗口的布局参数,以及调用成员函数prepareAddWindowLw来检查当前应用程序进程请求增长的窗口是不是合法的。若是不是合法的,即变量res的值不等于WindowManagerImpl.ADD_OKAY,那么函数就不会继续向前执行,而直接返回错误码res。
WindowState对象的建立过程咱们在接下来的Step 4中再分析,如今咱们继续向前看代码:
if (outInputChannel != null) { String name = win.makeInputChannelName(); InputChannel[] inputChannels = InputChannel.openInputChannelPair(name); win.mInputChannel = inputChannels[0]; inputChannels[1].transferToBinderOutParameter(outInputChannel); mInputManager.registerInputChannel(win.mInputChannel); }
这段代码是用建立IO输入事件链接通道的,以便正在增长的窗口能够接收到系统所发生的键盘和触摸屏事件,能够参考前面Android应用程序键盘(Keyboard)消息处理机制分析一文,这里再也不详述。
咱们继续向前看代码:
if (addToken) { mTokenMap.put(attrs.token, token); mTokenList.add(token); } win.attach(); mWindowMap.put(client.asBinder(), win); if (attrs.type == TYPE_APPLICATION_STARTING && token.appWindowToken != null) { token.appWindowToken.startingWindow = win; }
这段代码首先检查变量addToken的值是否等于true。若是等于true的话,那么就说明变量token所指向的一个WindowToken对象是在前面新建立的。在这种状况下,就须要将这个新建立的WindowToken对象分别添加到WindowManagerService类的成员变量mTokeMap和mTokenList分别描述的一个HashMap和一个ArrayList中去。
这段代码接下来再调用前面所建立的一个WindowState对象win的成员函数attach来为当前正在增长的窗口建立一个用来链接到SurfaceFlinger服务的SurfaceSession对象。有了这个SurfaceSession对象以后,当前正在增长的窗口就能够和SurfaceFlinger服务通讯了。在接下来的Step 5中,咱们再详细分析WindowState类的成员函数attach的实现。
这段代码最后还作了两件事情。第一件事情是将前面所建立的一个WindowState对象win添加到WindowManagerService类的成员变量mWindowMap所描述的一个HashMap中,这是以参数所描述的一个类型为W的Binder代理对象的IBinder接口来键值来保存的。第二件事情是检查当前正在增长的是不是一个起始窗口,若是是的话,那么就会将前面所建立的一个WindowState对象win设置到用来描述它所属的Activity组件的一个AppWindowToken对象的成员变量startingWindow中去,这样系统就能够在显示这个Activity组件的窗口以前,先显示该起始窗口。
咱们继续向前看代码:
boolean imMayMove = true; if (attrs.type == TYPE_INPUT_METHOD) { mInputMethodWindow = win; addInputMethodWindowToListLocked(win); imMayMove = false; } else if (attrs.type == TYPE_INPUT_METHOD_DIALOG) { mInputMethodDialogs.add(win); addWindowToListInOrderLocked(win, true); adjustInputMethodDialogsLocked(); imMayMove = false; } else { addWindowToListInOrderLocked(win, true); if (attrs.type == TYPE_WALLPAPER) { ...... adjustWallpaperWindowsLocked(); } else if ((attrs.flags&FLAG_SHOW_WALLPAPER) != 0) { adjustWallpaperWindowsLocked(); } }
WindowManagerService类有一个成员变量mWindows,它是一个类型为WindowState的ArrayList,保存在它里面的WindowState对象所描述的窗口是按照它们的Z轴坐标从小到大的顺序来排列的。如今咱们须要考虑将用来描述当前正在增长的窗口的WindowState对象win添加到这个ArrayList的合适位置中去。咱们分三种状况考虑。
若是当前正在增长的窗口是一个输入法窗口,那么WindowManagerService服务就须要按照Z轴坐标从大到小的顺序来检查当前是哪个窗口是须要输入法窗口的。找到了这个位于最上面的须要输入法窗口的窗口以后,就能够将输入法窗口放置在它的上面了,便可以将WindowState对象win添加到WindowManagerService类的成员变量mWindows所描述的一个ArrayList的合适位置了。这两个操做是经过调用WindowManagerService类的成员函数addInputMethodWindowToListLocked来实现的。同时,WindowManagerService类的成员变量mInputMethodWindow指向的是系统当前正在使用的输入法窗口,所以,在这种状况下,咱们还须要将WindowState对象win保存在它里面。
若是当前正在添加的窗口是一个输入法对话框,那么WindowManagerService服务就须要作两件事情。第一件事情是将WindowState对象win添加到WindowManagerService类的成员变量mWindows所描述的一个ArrayList中去,这是经过调用WindowManagerService类的成员函数addWindowToListInOrderLocked来实现的。第二件事情是调用WindowManagerService类的成员函数adjustInputMethodDialogsLocked来调整正在增长的输入法对话框在WindowManagerService类的成员变量mWindows所描述的一个ArrayList中的位置,使得它位于输入法窗口的上面。
在上述两种状况下,系统的输入法窗口和输入法对话框的位置都是获得合适的调整的了,所以,这段代码就会将变量imMayMove的值设置为false,表示后面不须要再调整输入法窗口和输入法对话框的位置了。
若是当前正在添加的窗口是一个应用程序窗口或者壁纸窗口,那么WindowManagerService服务都须要将WindowState对象win添加到WindowManagerService类的成员变量mWindows所描述的一个ArrayList中去。添加完成以后,若是发现当前添加的窗口是一个壁纸窗口或者当前添加的是一个须要显示壁纸的应用程序窗口,那么WindowManagerService服务还须要进一步调整系统中已经添加了的壁纸窗口的Z轴位置,使得它们位于最上面的须要显示壁纸的窗口的下面,这是经过调用WindowManagerService类的成员函数adjustWallpaperWindowsLocked来实现的。
咱们继续向前看代码:
win.mEnterAnimationPending = true; mPolicy.getContentInsetHintLw(attrs, outContentInsets); if (mInTouchMode) { res |= WindowManagerImpl.ADD_FLAG_IN_TOUCH_MODE; } if (win == null || win.mAppToken == null || !win.mAppToken.clientHidden) { res |= WindowManagerImpl.ADD_FLAG_APP_VISIBLE; }
这段代码作了四件事情。
第一件事情是将前面所建立的一个WindowState对象win的成员变量mEnterAnimationPending的值设置为true,表示当前正在增长的窗口须要显示一个进入动画。
第二件事情是调用WindowManagerService类的成员变量mPolicy所描述的一个窗口管理策略器的成员函数getContentInsetHintLw来得到当前正在增长的窗口的UI内容边衬大小,即当前正在增长的窗口能够在屏幕中所得到的用来显示UI内容的区域的大小,这一般是要排除屏幕边框和状态栏等所占据的屏幕区域。
第三件事情是检查WindowManagerService类的成员变量mInTouchMode的值是否等于true。若是等于true的话,那么就说明系统运行在触摸屏模式中,这时候这段代码就会将返回值res的WindowManagerImpl.ADD_FLAG_IN_TOUCH_MODE位设置为1。
第四件事情是检查当前正在增长的窗口是不是处于可见的状态。从第二个if语句能够看出,因为WindowState对象win的值在这里不能够等于null,所以,这里只有两种状况下,前正在增长的窗口是处于可见状态的。第一种状况是WindowState对象的成员变量mAppToken的值等于null,这代表当前正在增长的窗口不是一个应用程序窗口,即不是一个Activity组件窗口,那么它就有多是一个子窗口。因为子窗口一般是在其父窗口处于可见的状态下才会建立的,所以,这个子窗口就须要立刻显示出来的,即须要将它的状态设置为可见的。第二种状况是WindowState对象的成员变量mAppToken的值不等于null,这代表当前正在增长的窗口是一个应用程序窗口。在这种状况下,WindowState对象的成员变量mAppToken指向的就是一个AppWindowToken对象。当这个AppWindowToken对象的成员变量clientHidden的值等于false的时候,就代表它所描述的一个Activity组件是处于可见状态的,所以,这时候就须要将该Activity组件的窗口(即当前正在增长的窗口)的状态设置为可见的。在当前正在增长的窗口是处于可见状态的状况下,这段代码就会将返回值res的WindowManagerImpl.ADD_FLAG_APP_VISIBLE位设置为1。
咱们继续向前看最后一段代码:
boolean focusChanged = false; if (win.canReceiveKeys()) { focusChanged = updateFocusedWindowLocked(UPDATE_FOCUS_WILL_ASSIGN_LAYERS); if (focusChanged) { imMayMove = false; } } if (imMayMove) { moveInputMethodWindowsIfNeededLocked(false); } assignLayersLocked(); ...... if (focusChanged) { finishUpdateFocusedWindowAfterAssignLayersLocked(); } ...... } ... return res; } ...... }
最后一段代码作了如下五件事情。
第一件事情是检查当前正在增长的窗口是不是可以接收IO输入事件的,即键盘输入事件或者触摸屏输入事件。若是能够的话,那么就须要调用WindowManagerService类的成员函数updateFocusedWindowLocked来调整系统当前得到输入焦点的窗口,由于当前正在增长的窗口可能会成为新的能够得到输入焦点的窗口。若是WindowManagerService类的成员函数updateFocusedWindowLocked的返回值等于true,那么就代表系统当前得到输入焦点的窗口发生了变化。在这种状况下,WindowManagerService类的成员函数updateFocusedWindowLocked也会顺便调整输入法窗口的位置,使得它位于系统当前得到输入焦点的窗口的上面,所以,这时候这段代码也会将变量imMayMove的值设置为false,表示接下来不用调整输入法窗口的位置了。
第二件事情是检查变量imMayMove的值是否等于true。若是等于true的话,那么就说明当前正在增长的窗口可能已经影响到系统的输入法窗口的Z轴位置了,所以,这段代码就须要调用WindowManagerService类的成员函数moveInputMethodWindowsIfNeededLocked来从新调整调整输入法窗口的Z轴位置,使得它能够位于最上面的那个须要输入法窗口的窗口的上面。
第三件事情是调用WindowManagerService类的成员函数assignLayersLocked来调整系统中全部窗口的Z轴位置,这也是由于当前正在增长的窗口可能已经影响到系统中全部窗口的Z轴位置了。例如,假如当前增长的是一个正在启动的Activity组件的窗口,那么这个窗口的Z轴位置就应该是最大的,以即可以在最上面显示。又如,假如当前增长的是一个子窗口,那么这个子窗口就应该位于它的父窗口的上面。这些都要求从新调整系统中全部窗口的Z轴位置,以便每个窗口均可以在一个正确的位置上来显示。
第四件事情检查变量focusChanged的值是否等于true。若是等于true的话,那么就说明系统中得到输入焦点的窗口已经发生了变化。在这种状况下,这段代码就会调用WindowManagerService类的成员函数finishUpdateFocusedWindowAfterAssignLayersLocked来通知系统IO输入管理器,新的得到焦点的窗口是哪个,以便系统IO输入管理器接下来能够将键盘输入事件或者触摸屏输入事件分发给新的得到焦点的窗口来处理。WindowManagerService类的成员函数finishUpdateFocusedWindowAfterAssignLayersLocked的实现能够参考前面Android应用程序键盘(Keyboard)消息处理机制分析一文,这里再也不详述。
第五件事情是变量res的值返回给应用程序进程,以便它能够知道请求WindowManagerService服务增长一个窗口的执行状况。
至此,WindowManagerService类的成员函数addWindow的实现就分析完成了,接下来咱们就继续分析WindowState类的构造函数和成员函数attach的实现,以即可以了解一个WindowSate对象及其相关联的一个SurfaceSession对象的建立过程。
Step 4. new WindowState
这个函数定义在文件frameworks/base/services/java/com/android/server/WindowManagerService.java中,它的实现比较长,咱们分段来阅读:
public class WindowManagerService extends IWindowManager.Stub implements Watchdog.Monitor { ...... private final class WindowState implements WindowManagerPolicy.WindowState { ...... WindowState(Session s, IWindow c, WindowToken token, WindowState attachedWindow, WindowManager.LayoutParams a, int viewVisibility) { mSession = s; mClient = c; mToken = token; mAttrs.copyFrom(a); mViewVisibility = viewVisibility; DeathRecipient deathRecipient = new DeathRecipient(); mAlpha = a.alpha;
这段代码初始化WindowState类的如下六个成员变量:
-mSession:指向一个类型为Session的Binder本地对象,使用参数s来初始化,表示当前所建立的WindowState对象是属于哪个应用程序进程的。
-mClient:指向一个实现了IWindow接口的Binder代理对象,它引用了运行在应用程序进程这一侧的一个类型为W的Binder本地对象,使用参数c来初始化,经过它能够与运行在应用程序进程这一侧的Activity组件进行通讯。
-mToken:指向一个WindowToken对象,使用参数token来初始化,经过它就能够知道惟一地标识一个窗口。
-mAttrs:指向一个WindowManager.LayoutParams对象,使用参数a来初始化,经过它就能够知道当前当前所建立的WindowState对象所描述的窗口的布局参数。
-mViewVisibility:这是一个整型变量,使用参数viewVisibility来初始化,表示当前所建立的WindowState对象所描述的窗口视图的可见性。
-mAlpha:这是一个浮点数,使用参数a所描述的一WindowManager.LayoutParams对象的成员变量alpha来初始化,表示当前所建立的WindowState对象所描述的窗口的Alpha通道。
此外,这段代码还建立了一个类型为DeathRecipient的死亡通知接收者deathRecipient,它是用来监控参数c所引用的一个类型为W的Binder本地对象的生命周期的。当这个Binder本地对象死亡的时候,就意味着当前所建立的WindowState对象所描述的窗口所在的应用程序进程已经退出了。接下来的这段代码就是用来注册死亡通知接收者deathRecipient的,以下所示:
try { c.asBinder().linkToDeath(deathRecipient, 0); } catch (RemoteException e) { mDeathRecipient = null; mAttachedWindow = null; mLayoutAttached = false; mIsImWindow = false; mIsWallpaper = false; mIsFloatingLayer = false; mBaseLayer = 0; mSubLayer = 0; return; } mDeathRecipient = deathRecipient;
注册完成以后,前面所建立的死亡通知接收者deathRecipient就会保存在WindowState类的成员变量mDeathRecipientk 。
咱们继续向前看代码:
if ((mAttrs.type >= FIRST_SUB_WINDOW && mAttrs.type <= LAST_SUB_WINDOW)) { // The multiplier here is to reserve space for multiple // windows in the same type layer. mBaseLayer = mPolicy.windowTypeToLayerLw( attachedWindow.mAttrs.type) * TYPE_LAYER_MULTIPLIER + TYPE_LAYER_OFFSET; mSubLayer = mPolicy.subWindowTypeToLayerLw(a.type); mAttachedWindow = attachedWindow; mAttachedWindow.mChildWindows.add(this); mLayoutAttached = mAttrs.type != WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG; mIsImWindow = attachedWindow.mAttrs.type == TYPE_INPUT_METHOD || attachedWindow.mAttrs.type == TYPE_INPUT_METHOD_DIALOG; mIsWallpaper = attachedWindow.mAttrs.type == TYPE_WALLPAPER; mIsFloatingLayer = mIsImWindow || mIsWallpaper; } else { // The multiplier here is to reserve space for multiple // windows in the same type layer. mBaseLayer = mPolicy.windowTypeToLayerLw(a.type) * TYPE_LAYER_MULTIPLIER + TYPE_LAYER_OFFSET; mSubLayer = 0; mAttachedWindow = null; mLayoutAttached = false; mIsImWindow = mAttrs.type == TYPE_INPUT_METHOD || mAttrs.type == TYPE_INPUT_METHOD_DIALOG; mIsWallpaper = mAttrs.type == TYPE_WALLPAPER; mIsFloatingLayer = mIsImWindow || mIsWallpaper; }
这段代码初始化WindowState类的如下七个成员变量:
-mBaseLayer:这是一个整型变量,用来描述一个窗口的基础Z轴位置值,这个值是与窗口类型相关的。对于子窗口来讲,它的值由父窗口的基础Z轴位置值乘以常量TYPE_LAYER_MULTIPLIER再加固定偏移量TYPE_LAYER_OFFSET获得;对于非子窗口来讲,它的值就是由窗口的类型来决定的。一个窗口的基础Z轴位置值是经过调用WindowManagerService类的成员变量mPolicy所描述的一个窗口管理策略器的成员函数windowTypeToLayerLw来得到的,而窗口管理策略器的成员函数windowTypeToLayerLw主要是根据窗口的类型来决定它的基础Z轴位置值的。
-mSubLayer:这是一个整型变量,用来描述一个子窗口相对其父窗口的Z轴偏移值。对于非子窗口来讲,这个值固定为0;对于子窗口来讲,这个值是由WindowManagerService类的成员变量mPolicy所描述的一个窗口管理策略器的成员函数subWindowTypeToLayerLw来得到的,而窗口管理策略器的成员函数subWindowTypeToLayerLw主要是根据子窗口的类型来决定它相对其父窗口的Z轴偏移值的。
-mAttachedWindow:指向一个WindowState对象,用来描述一个子窗口的父窗口。对于非子窗口来讲,这个值固定为null;对于子窗口来讲, 这个值使用参数attachedWindow来初始化。若是当前所建立的WindowState对象所描述的窗口是一个子窗口,那么这个子窗口还会被添加用来描述它的父窗口的一WindowState对象的成员变量mChildWindows所描述的一个子窗口列表中去。
-mLayoutAttached:这是一个布尔变量,用来描述一个子窗口的视图是不是嵌入在父窗口的视图里面的。对于非子窗口来讲,这个值固定为false;对于子窗口来讲,这个值只有子窗口的类型是非对话框时,它的值才会等于true,不然都等于false。
-mIsImWindow:这是一个布尔变量,表示当前所建立的WindowState对象所描述的窗口是不是一个输入法窗口或者一个输入法对话框。
-mIsWallpaper :这是一个布尔变量,表示当前所建立的WindowState对象所描述的窗口是不是一个壁纸窗口。
-mIsFloatingLayer :这是一个布尔变量,表示当前所建立的WindowState对象所描述的窗口是不是一个浮动窗口。当一个窗口是一个输入法窗口、输入法对话框口或者壁纸窗口时,它才是一个浮动窗口。
咱们继续向前看代码:
WindowState appWin = this; while (appWin.mAttachedWindow != null) { appWin = mAttachedWindow; } WindowToken appToken = appWin.mToken; while (appToken.appWindowToken == null) { WindowToken parent = mTokenMap.get(appToken.token); if (parent == null || appToken == parent) { break; } appToken = parent; } mRootToken = appToken; mAppToken = appToken.appWindowToken;
这段代码主要用来初始化成员变量mRootToken和mAppToken。
成员变量mRootToken的类型为WindowToken,用来描述当前所建立的WindowState对象所描述的窗口的根窗口。若是当前所建立的WindowState对象所描述的窗口是一个子窗口,那么就先找到它的父窗口,而后再找到它的父窗口所属的应用程序窗口,即Activity组件窗口,这时候找到的Activity组件窗口就是一个根窗口。若是当前所建立的WindowState对象所描述的窗口是一个子窗口,可是它不属于任何一个应用程序窗口的,那么它的父窗口就是一个根窗口。若是当前所建立的WindowState对象所描述的窗口不是一个子窗口,而且它也不属于一个应用程序窗口的,那么它自己就是一个根窗口。
成员变量mAppToken的类型为AppWindowToken,只有当成员变量mRootToken所描述的一个根窗口是一个应用程序窗口时,它的值才不等于null。
咱们继续向前看最后一段代码:
mSurface = null; mRequestedWidth = 0; mRequestedHeight = 0; mLastRequestedWidth = 0; mLastRequestedHeight = 0; mXOffset = 0; mYOffset = 0; mLayer = 0; mAnimLayer = 0; mLastLayer = 0; } ...... } ...... }
这段代码将如下十个成员变量的值设置为null或者0:
-mSurface:指向一个mSurface对象,用来描述窗口的绘图表面。
-mRequestedWidth:这是一个整型变量,用来描述应用程序进程所请求的窗口宽度。
-mRequestedHeight:这是一个整型变量,用来描述应用程序进程所请求的窗口高度。
-mLastRequestedWidth:这是一个整型变量,用来描述应用程序进程上一次所请求的窗口宽度。
-mLastRequestedHeight:这是一个整型变量,用来描述应用程序进程上一次所请求的窗口高度。
-mXOffset:这是一个整型变量,用来描述壁纸窗口相对屏幕在X轴上的偏移量,对其它类型的窗口为说,这个值等于0。
-mYOffset:这是一个整型变量,用来描述壁纸窗口相对屏幕在Y轴上的偏移量,对其它类型的窗口为说,这个值等于0。
-mLayer:这是一个整型变量,用来描述窗口的Z轴位置值。
-mAnimLayer:这是一个整型变量,用来描述窗口的Z轴位置值,它的值可能等于mLayer的值,可是在如下四种状况中不相等。当一个窗口关联有一个目标窗口的时候,那么它的值就等于mLayer的值加上目标窗口指定的一个动画层调整值;当一个窗口的根窗口是一个应用程序窗口时,那么它的值就等于mLayer的值加上根窗口指定的一个动画层调整值;当一个窗口是一个输入法窗口时,那么它的值就等于mLayer的值加上系统设置的输入法动画层调整值;当一个窗口是壁纸窗口时,那么它的值就等于mLayer的值加上系统设置的壁纸动画层调整值。
-mLastLayer:这是一个整型变量,用描述窗口上一次所使用的mAnimLayer的值。
至此,咱们就分析完成WindowState的构造函数的实现了,返回到前面的Step 3中,即WindowManagerService类的成员函数addWindow中,接下来就会继续调用前面所建立的一个WindowState对象的成员函数attach来建立一个关联的SurfaceSession对象,以即可以用来和SurfaceFlinger服务通讯。
Step 5. WindowState.attach
public class WindowManagerService extends IWindowManager.Stub implements Watchdog.Monitor { ...... private final class WindowState implements WindowManagerPolicy.WindowState { final Session mSession; ...... void attach() { ...... mSession.windowAddedLocked(); } ...... } ...... }
这个函数定义在文件frameworks/base/services/java/com/android/server/WindowManagerService.java中。
WindowState类的成员变量mSession指向的是一个Session对象,这个Session对象就是用来链接应用程序进程和WindowManagerService服务,WindowState类的成员函数attach调用它的成员函数windowAddedLocked来检查是否须要为当前正在请求增长窗口的应用程序进程建立一个SurfaceSession对象。
接下来,咱们继续分析Session类的成员函数windowAddedLocked的实现。
Step 6. Session.windowAddedLocked
public class WindowManagerService extends IWindowManager.Stub implements Watchdog.Monitor { ...... /** * All currently active sessions with clients. */ final HashSet<Session> mSessions = new HashSet<Session>(); ...... private final class Session extends IWindowSession.Stub implements IBinder.DeathRecipient { ...... SurfaceSession mSurfaceSession; int mNumWindow = 0; ...... void windowAddedLocked() { if (mSurfaceSession == null) { ...... mSurfaceSession = new SurfaceSession(); ...... mSessions.add(this); } mNumWindow++; } ...... } ...... }
这个函数定义在文件frameworks/base/services/java/com/android/server/WindowManagerService.java中。
Session类的成员变量mSurfaceSession指向的是一个SurfaceSession对象,这个SurfaceSession对象是WindowManagerService服务用来与SurfaceSession服务创建链接的。Session类的成员函数windowAddedLocked首先检查这个成员变量的值是否等于null。若是等于null的话,那么就说明WindowManagerService服务还没有为当前正在请求增长窗口的应用程序进程建立一个用来链接SurfaceSession服务的SurfaceSession对象,所以,Session类的成员函数windowAddedLocked就会建立一个SurfaceSession对象,而且保存在成员变量mSurfaceSession中,而且将正在处理的Session对象添加WindowManagerService类的成员变量mSession所描述的一个HashSet中去,表示WindowManagerService服务又多了一个激活的应用程序进程链接。
Session类的另一个成员变量mNumWindow是一个整型变量,用来描述当前正在处理的Session对象内部包含有多少个窗口,即运行在引用了当前正在处理的Session对象的应用程序进程的内部的窗口的数量。每当运行在应用程序进程中的窗口销毁时,该应用程序进程就会通知WindowManagerService服务移除用来描述该窗口状态的一个WindowState对象,而且通知它所引用的Session对象减小其成员变量mNumWindow的值。当一个Session对象的成员变量mNumWindow的值减小为0时,就说明该Session对象所描述的应用程序进程链接已经不须要了,所以,该Session对象就能够杀掉其成员变量mSurfaceSession所描述的一个SurfaceSession对象,以即可以断开和SurfaceSession服务的链接。
接下来,咱们就继续分析SurfaceSession类的构造函数的实现,以即可以了解一个SurfaceSession对象是如何与SurfaceSession服务创建链接的。
Step 7. new SurfaceSession
public class SurfaceSession { /** Create a new connection with the surface flinger. */ public SurfaceSession() { init(); } ...... private native void init(); ...... private int mClient; }
这个函数定义在文件frameworks/base/core/java/android/view/SurfaceSession.java中。
SurfaceSession类的构造函数的实现很简单,它只是调用了另一个成员函数init来执行一些初始化操做,其实就是用来初始化SurfaceSession类的成员变量mClient。
SurfaceSession类的成员函数init是一个JNI方法,它是由C++层的函数SurfaceSession_init来实现的,以下所示:
static void SurfaceSession_init(JNIEnv* env, jobject clazz) { sp<SurfaceComposerClient> client = new SurfaceComposerClient; client->incStrong(clazz); env->SetIntField(clazz, sso.client, (int)client.get()); }
这个函数定义在文件frameworks/base/core/jni/android_view_Surface.cpp中。
在分析函数SurfaceSession_init的实现以前,咱们首先看看全局变量sso的定义,以下所示:
struct sso_t { jfieldID client; }; static sso_t sso;
它是一个类型为sso_t的结构体变量,它的成员变量client描述的是SurfaceSession类的成员变量mClient在SurfaceSession类中的偏移量:
void nativeClassInit(JNIEnv* env, jclass clazz) { ...... jclass surfaceSession = env->FindClass("android/view/SurfaceSession"); sso.client = env->GetFieldID(surfaceSession, "mClient", "I"); ...... }
回到函数SurfaceSession_init中,它首先建立一个SurfaceComposerClient对象client,接着再增长这个SurfaceComposerClient对象的强引用计数,由于再接下来会将这个SurfaceComposerClient对象的地址值保存在参数clazz所描述的一个SurfaceSession对象的成员变量mClient中,这至关因而参数clazz所描述的一个SurfaceSession对象引用了刚才所建立的SurfaceComposerClient对象client。
在前面Android应用程序与SurfaceFlinger服务的关系概述和学习计划这一系列的文章中,咱们已经分析过SurfaceComposerClient类的做用了,这主要就是用来在应用程序进程和SurfaceFlinger服务之间创建链接的,以便应用程序进程能够为运行在它里面的应用程序窗口请求SurfaceComposerClient建立绘制表面(Surface)的操做等。
这样,每个Java层的SurfaceSession对象在C++层就都有一个对应的SurfaceComposerClient对象,所以,Java层的应用程序就能够经过SurfaceSession类来和SurfaceFlinger服务创建链接。
至此,咱们就分析完成一个WindowState对象的建立过程了,经过这个过程咱们就能够知道,每个Activity组件窗口在WindowManagerService服务内部都有一个对应的WindowState对象,用来描述它的窗口状态。
至此,咱们也分析完成Android应用程序窗口与WindowManagerService服务的链接过程了。从这个链接过程以及前面Android应用程序窗口(Activity)的窗口对象(Window)的建立过程分析和Android应用程序窗口(Activity)的视图对象(View)的建立过程分析这两篇文章,咱们就能够知道,为了实现一个Activity组件的UI,不管是应用程序进程,仍是WindowManagerService,都作了大量的工做,例如,应用程序进程为它建立一个窗口(Window)对象、一个视图(View)对象、一个ViewRoot对象、一个W对象,WindowManagerService服务为它建立一个AppWindowToken对象和一个WindowState对象。此外,WindowManagerService服务还为一个Activity组件所运行在的应用程序进程建立了一个Session对象。理解这些对象的实现以及做用对咱们了解Android应用程序窗口的实现框架以及WindowManagerService服务的实现原理都是很是重要的。
虽然到目前为止,咱们已经为Android应用程序窗口建立了不少对象,可是咱们仍然还有一个最重要的对象尚未建立,那就是Android应用程序窗口的绘图表面,即用来渲染UI的Surface尚未建立。从前面Android应用程序与SurfaceFlinger服务的关系概述和学习计划这一系列的文章能够知道,这个Surface是要请求SurfaceFlinger服务来建立的,所以,在接下来的一篇文章中,咱们就将继续分析Android应用程序窗口的绘图表面(Surface)的建立过程,敬请关注!