Activity窗口大小计算
frameworks/base/core/java/android/view/ViewRootImpl.java:
这段代码用来获得Activity窗口的当前宽度desiredWindowWidth和当前高度desiredWindowHeight:
Activity窗口当前的宽度和高度是保存在ViewRootImpl类的成员变量mWinFrame中的。ViewRootImpl类的另外两个成员变量mWidth和mHeight也是用来描述Activity窗口当前的宽度和高度的,但是它们的值是由应用程序进程上一次主动请求WindowManagerService服务计算得到的,并且会一直保持不变到应用程序进程下一次再请求WindowManagerService服务来重新计算为止。Activity窗口的当前宽度和高度有时候是被WindowManagerService服务主动请求应用程序进程修改的,修改后的值就会保存在ViewRootImpl类的成员变量mWinFrame中,它们可能会与ViewRoot类的成员变量mWidth和mHeight的值不同。
如果Activity窗口是第一次被请求执行测量、布局和绘制操作,即ViewRootImpl类的成员变量mFirst的值等于true,那么它的当前宽度desiredWindowWidth和当前高度desiredWindowHeight就等于屏幕的宽度和高度,否则的话,它的当前宽度desiredWindowWidth和当前高度desiredWindowHeight就等于保存在ViewRootImpl类的成员变量mWinFrame中的宽度和高度值。
如果Activity窗口不是第一次被请求执行测量、布局和绘制操作,并且Activity窗口主动上一次请求WindowManagerService服务计算得到的宽度mWidth和高度mHeight不等于Activity窗口的当前宽度desiredWindowWidth和当前高度desiredWindowHeight,那么就说明Activity窗口的大小发生了变化,这时候变量windowSizeMayChange的值就会被标记为true,以便接下来可以对Activity窗口的大小变化进行处理。
ViewRoot类的成员变量mAttachInfo指向的一个AttachInfo对象,这个AttachInfo对象用来描述Activity窗口的属性,例如,这个AttachInfo对象的成员变量mContentInsets和mVisibleInsets分别用来描述Activity窗口上一次主动请求WindowManagerService服务计算得到的内容边衬大小和可见边衬大小,即Activity窗口的当前内容边衬大小和可见边衬大小。ViewRoot类的成员变量mPendingContentInsets和mPendingVisibleInsets也是用来描述Activity窗口的内容边衬大小和可见边衬大小的,不过它们是由WindowManagerService服务主动请求Activity窗口设置的,但是尚未生效。 这段代码用来在Activity窗口主动请求WindowManagerService服务计算大小之前,对它的顶层视图进行一次测量操作。
这段代码主要是做两件事情。
第一件事情是检查是否需要处理Activity窗口的大小变化事件。如果满足以下条件,那么就需要处理,即将变量windowShouldResize的值设置为true:
1. ViewRoot类的成员变量layoutRequest的值等于true,这说明应用程序进程正在请求对Activity窗口执行一次测量、布局和绘制操作;
2. 变量windowSizeMayChange的值等于true,这说明前面检测到了Activity窗口的大小发生了变化;
3. 前面我们已经Activity窗口的顶层视图host的大小重新进行了测量。如果测量出来的宽度host.mMeasuredWidth和高度host.mMeasuredHeight和Activity窗口的当前宽度mWidth和高度mHeight一样,那么即使条件1和条件2能满足,那么也是可以认为是Activity窗口的大小是没有发生变化的。换句话说,只有当测量出来的大小和当前大小不一致时,才认为Activity窗口大小发生了变化。另一方面,如果测量出来的大小和当前大小一致,但是Activity窗口的大小被要求设置成WRAP_CONTENT,即设置成和屏幕的宽度desiredWindowWidth和高度desiredWindowHeight一致,但是WindowManagerService服务请求Activity窗口设置的宽度frame.width()和高度frame.height()与它们不一致,而且与Activity窗口上一次请求WindowManagerService服务计算的宽度mWidth和高度mHeight也不一致,那么也是认为Activity窗口大小发生了变化的。
第二件事情是检查Activity窗口是否需要指定有额外的内容边衬区域和可见边衬区域。如果有的话,那么变量attachInfo所指向的一个AttachInfo对象的成员变量mTreeObserver所描述的一个TreeObserver对象的成员函数hasComputeInternalInsetsListerner的返回值ComputeInternalInsets就会等于true。Activity窗口指定额外的内容边衬区域和可见边衬区域是为了放置一些额外的东西。
这段代码在满足下面的条件之一的情况下执行的:
1. Activity窗口是第一次执行测量、布局和绘制操作,即ViewRoot类的成员变量mFirst的值等于true。
2. 前面得到的变量windowShouldResize的值等于true,即Activity窗口的大小的确是发生了变化。
3. ivity窗口的可见性发生了变化,即变量viewVisibilityChanged的值等于true。
4. Activity窗口属性发生了变化,即变量params指向了一个WindowManager.LayoutParams对象。
在满足上述条件之一,当前面得到的变量computesInternalInsets等于true时,那么变量insetsPending的值就会等于true,表示Activity窗口有额外的内容区域边衬和可见区域边衬等待指定。
主要就是调用ViewRoot类的另外一个成员函数relayoutWindow来请求WindowManagerService服务计算Activity窗口的大小以及内容区域边衬大小和可见区域边衬大小。计算完毕之后,Activity窗口的大小就会保存在ViewRoot类的成员变量mWinFrame中,而Activity窗口的内容区域边衬大小和可见区域边衬大小分别保存在ViewRoot类的成员变量mPendingContentInsets和mPendingVisibleInsets中。
这段代码用来检查是否需要重新测量Activity窗口的大小。重新计算了一次之后,如果Activity窗口的属性lp表明需要对测量出来的宽度width和高度height进行扩展,即变量lp所指向的一个WindowManager.LayoutParams对象的成员变量horizontalWeight和verticalWeight的值大于0.0,那么就需要对Activity窗口的顶层视图host的最大可用空间进行扩展后再进行一次测量工作。
接下来,分析ViewRootImpl类的成员函数relayoutWindow的实现,以便可以了解它是如何请求WindowManagerService服务计算Activity窗口的大小的。
ViewRootImpl类的静态成员变量sWindowSession是一个Binder代理对象,它引用了运行在WindowManagerService服务这一侧的一个Session对象,ViewRootImpl类的成员函数relayoutWindow通过调用这个Session对象的成员函数relayout来请求WindowManagerService服务计算Activity窗口的大小,其中,传递给WindowManagerService服务的参数包括:
1. ViewRootImpl类的成员变量mWindow,用来标志要计算的是哪一个Activity窗口的大小。
2. Activity窗口的顶层视图经过测量后得到的宽度和高度。注意,传递给WindowManagerService服务的宽度和高度是已经考虑了Activity窗口所设置的缩放因子了的。
3. Activity窗口的可见状态,即参数viewVisibility。
4. Activity窗口是否有额外的内容区域边衬和可见区域边衬等待告诉给WindowManagerService服务,即参数insetsPending。
5. ViewRootImpl类的成员变量mTmpFrame,这是一个输出参数,用来保存WindowManagerService服务计算后得到的Activity窗口的大小。
6. ViewRootImpl类的成员变量mPendingConfiguration,这是一个输出参数,用来保存WindowManagerService服务返回来的Activity窗口的配置信息。
得到了Activity窗口的大小以及内容区域边衬大小和可见区域边衬大小之后,如果Activity窗口是运行在兼容模式中,即ViewRootImpl类的成员变量mTranslator指向了一个Translator对象,那么就需要调用它的成员函数translateRectInScreenToAppWindow来对它们进行转换。