Android 11 setRequestedOrientation流程详解
7:30 pm
Tuesday, 5 July 2022 (GMT+8)
Time in Guangzhou, Guangdong Province, China
- 概述
- 流程
- 总结
概述
分析Android 11 setRequestedOrientation
流程。
流程
setRequestedOrientation()
会触发ActivityRecord
进入新的orientation及Configuraion,并触发Window Tree hierarchy整体变化,然后Binder回调新的configuration到ActivityThread
,由ActivityThread
内部完成Activity
的onConfigurationChanged()
回调,并借助ViewRootImpl
触发DecorView
进行绘制。
ActivityRecord.setRequestedOrientation
Activity
发起setRequestedOrientation()
,实际上是通过Binder调用ATMS
,并实际上调用了ActivityRecord.setRequestedOrientation()
。
ActivityRecord
更新了自己的orientation,并通过onConfigurationChanged()
、resolveOverrideConfiguraion()
更新Configuration,借助onDescendantOrientationChanged()
更新Window Tree hirerarchy。
在onConfigurationChanged()
时更新了merged conf,并调整Surface Position,调度Animation。
在Window Tree hierarchy完成处理后,ActivityRecord
调用ensureActivityConfiguration()
,更新last reported configuration,并判断是否需要重启Activity。通过ClientTransaction
借助Binder调度App configuration changed。
在App侧,会收到multi window mode change的回调(若有)、onConfigurationChanged()
的回调,并回调ViewRootImpl
的updateConfiguration()
。ViewRootImpl
会对DecorView
的Children回调dispatchConfigurationChanged()
,触发View Tree中各View的onConfiguraionChanged()
。并接着requestLayout()
->scheduleTraversal()
跟随ChoreoGrapher
的节奏进行一轮绘制流程。
ViewRootImpl、DecorView、PhoneWindow是在onCreate()时setContentView()中完成创建的。其中ViewRootImpl.mView即DecorView。
onDescendantOrientationChanged
作为Window Tree Hierarchy中的一员,ActivityRecord
在Orientation
变化时,会通过onDescendantOrientationChanged()
向层级中的Family Parents发出回调。对于Task
、TaskDisplayArea
该方法实现均是默认的父类实现,即回调parent的同名方法。最终处理该方法的是DisplayContent.onDescendantOrientationChanged()
。
DisplayContent.onDescendantOrientationChanged
DisplayContent
调用updateOrientation()
更新方向并返回对应的Configuration
(Conf不变则返回null,提前退出到上一级,返回值取决于用户有没有设置固定屏幕方向)。该过程中,通过handleTopActivityLaunchingInDifferentOrientation()
来根据条件检查是否会响应本次方向申请。
确定执行方向切换后,updateOrientation()
实际上是由DisplayRotation
的同名方法来实现的。它更新内部维护的rotation信息,并触发屏幕旋转动画。
然后,调用computeScreenConfiguration()
计算得到新的Configuration。接着调用updateDisplayOverrideConfigurationLocked()
更新DisplayContent
的override conf。该方法调用ATMS的updateDisplayOverrideConfigurationLocked()
和ensureConfigAndVisibilityAfterUpdate()
,最终调度完成ActivityRecord
和Activity
的conf更新。
DisplayRotation.updateRotationUnchecked()决定了屏幕方向并调度触发屏幕旋转动画。
ATMS.updateGlobalConfigurationLocked
DisplayContent.onDescendantOrientationChanged()
流程中,通过DisplayRotation
完成屏幕旋转后,就会借助ATMS来更新Configuration。
调用ATMS的updateGlobalConfigurationLocked()
更新Global Conf。该流程计算configuration,并回调RootWindowContainer.onConfigurationChanged()
——触发整个Window Tree Hierarchy的更新,
接着触发ATMS所在进程的ActivityThread内的资源更新、AttributeCache更新。
然后遍历系统全局所有的WindowProcessController
并回调onConfigurationChanged()
。
通过Handler调度AMS发出Configuration changed的广播。
最后调用Default DisplayContent.performDisplayOverrideConfigUpdate()
最后完成更新。
RootWindowContainer.onConfigurationChanged
RootWindowContainer
是全局Window Tree Hierarchy的根节点。它的onConfigurationChanged()
属于深度优先搜索,向层级叶子节点回调onConfigurationChanged()
。
整体流程主要完成更新Window Tree Configuration、更新WindowToken(ActivityRecord)、WindowContainer(WindowState)的MergedConfiguration、更新Surface Position、更新Resources等工作。
经过RootWindowContainer.onConfiguraionChanged()
完整流程后,系统内各个ConfiguraionContainer
、WindowContainer
、WindowToken
均计算并应用了对应的Configuration了。
这个时候,仅仅是完成了WMS内部的Configuration更新,还没有完成应用侧的更新,以及绘制流程使用的Configuration之更新(ViewRootImpl)
WindowProcessController.onConfigurationChanged
ATMS.updateGlobalConfigurationLocked()
在RootWindowContainer
完成整个Hierarchy的更新后,会回调系统全局所有WindowProcessController
的onConfigurationChanged()
。
ATMS.mProcessMap: WindowProcessControllerMap记录了所有正在运行的Android应用进程。
WindowProcessController.onConfigurationChanged()
的流程相对RootWindowContainer
比较简单些。它的主要工作是将新的Configuration
通过ClientTransaction
借助Binder发回ActivityThread
,其中触发App侧的Resource更新、onConfiguraionChanged()
的回调。
DisplayContent.performDisplayOverrideConfigUpdate
紧接着,调用performDisplayOverrideConfigUpdate()
来更新DisplayContent
及Window Tree Hierarchy的DisplayInfo
、Configuration
,并作为Global Configuration设置到RootWindowContainer
。
ATMS.ensureConfigAndVisibilityAfterUpdate
ATMS.updateGlobalConfiguraionLocked()
完成后,紧接着DisplayContent.onDescendantOrientationChanged()
会调用ATMS.ensureConfigAndVisibilityAfterUpdate()
来检查所有Activity的可见性并更新它们的状态(包括Configuration、生命周期、可见性)。
ATMS.continueWindowLayout
当上面所有工作完成后,即可解除一开始就暂停的layout,调用ATMS.continueWindowLayout()
将上面configuration changed后的变化应用起来,并继续窗口系统的绘制和显示。
在执行Traversal的过程中,对于Configuration改变导致的边界、大小发生变化的窗口即mResizingWindows
内的窗口,会通过IWindow
这个Binder回调到应用(ViewRootImpl.W
),并最终回调到ViewRootImpl.dispatchResized()
。该方法内部会更新传入的MergedOverrideConfiguration
,并根据新的frame,通过setFrame()
流程中的requestLayout()
完成一次绘制,在应用界面上展现新的适配Configuration的画面。
ViewRootImpl.dispatchResized
当一次Configuration Changed后,窗口大小或位置发生变化的ViewRootImpl
会在Window Layout过程中被从Binder回调dispatchResized()
。该方法实际上发送Message到Handler。
在对应Message处理流程中,会通过sConfigCallbacks
回调到ActivityThread
,主要更新ResourcesManager
的Config,并回调ActivityThread.handleConfigurationChanged()
。
接着ViewRootImpl
记录自身的Last Reported Merged Config,并调用updateConfiguration()
来更新DecorView
及其Children的Config。
最后,ViewRootImpl
将Frame更新为dispatchResized()
传入的frame,并通过requestLayout
触发Traversal。
Traversal依赖Handler + Choreographer
的回调,最终在帧回调触发时通过relayoutWindow
重新测量、摆放、绘制控件,将应用画面刷新,以新的MergedConfiguration来展示新的应用画面。
总结
Android 11在setRequestedOrientation()
相关流程中,对比Android 10,整体大框架改动不大。其中在App侧则近乎是0改动。但是由于Android 11将原Android 10分散在AMS、WMS的Window Container模型做了很大修改,仅剩下WMS相关逻辑,做了许多对象的修改并引入DisplayArea,从而实现为一个全新的模型,因此集中在WMS内的流程仍然有比较大的差异。
结构来看,整体流程分为三步。第一步,ActivityRecord
触发Window Tree Hierarchy向上的Config changed,再由DisplayContent
调度ATMS
应用新的Config,并接着调度RootWindowContainer
将新的Config向下应用到整个Window Tree Hierarchy。
第二步,通过WindowProcessController
将新的Configuration先发回到App侧,异步地更新App的Resource,并回调onConfigurationChanged()
。在WMS完成Config的计算、应用和分发后,让应用进程能根据新的Config做适配和准备。
最后,在Window Layout流程中,对发生了边界、大小变化的窗口,触发其View Tree的重新绘制逻辑(Traversal),最终使应用以新的Merged Config绘制控件,展示新的画面。
整个过程可以抽象为三步:1. WMS完成新的Config计算、应用并分发;2. 新的Config通知应用,目的是为了在第三步绘制之前让应用先更新好Resource、更新内部信息以正确绘制View;3. 触发Traversal,重绘View Tree。