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。