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内部完成ActivityonConfigurationChanged()回调,并借助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()的回调,并回调ViewRootImplupdateConfiguration()ViewRootImpl会对DecorView的Children回调dispatchConfigurationChanged(),触发View Tree中各View的onConfiguraionChanged()。并接着requestLayout()->scheduleTraversal()跟随ChoreoGrapher的节奏进行一轮绘制流程。

ViewRootImpl、DecorView、PhoneWindow是在onCreate()时setContentView()中完成创建的。其中ViewRootImpl.mView即DecorView。

Android 11 ActivityRecord.setRequestedOrientation流程

onDescendantOrientationChanged

作为Window Tree Hierarchy中的一员,ActivityRecordOrientation变化时,会通过onDescendantOrientationChanged()向层级中的Family Parents发出回调。对于TaskTaskDisplayArea该方法实现均是默认的父类实现,即回调parent的同名方法。最终处理该方法的是DisplayContent.onDescendantOrientationChanged()

Android11onDescentantOrientationChanged流程详解

DisplayContent.onDescendantOrientationChanged

DisplayContent调用updateOrientation()更新方向并返回对应的Configuration(Conf不变则返回null,提前退出到上一级,返回值取决于用户有没有设置固定屏幕方向)。该过程中,通过handleTopActivityLaunchingInDifferentOrientation()来根据条件检查是否会响应本次方向申请。

确定执行方向切换后,updateOrientation()实际上是由DisplayRotation的同名方法来实现的。它更新内部维护的rotation信息,并触发屏幕旋转动画。

然后,调用computeScreenConfiguration()计算得到新的Configuration。接着调用updateDisplayOverrideConfigurationLocked()更新DisplayContent的override conf。该方法调用ATMS的updateDisplayOverrideConfigurationLocked()ensureConfigAndVisibilityAfterUpdate(),最终调度完成ActivityRecordActivity的conf更新。

DisplayRotation.updateRotationUnchecked()决定了屏幕方向并调度触发屏幕旋转动画。

Android11DisplayContent.onDescendantOrientationChanged流程

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()最后完成更新。

Android ATMS.updateGlobalConfigurationLocked流程

RootWindowContainer.onConfigurationChanged

RootWindowContainer是全局Window Tree Hierarchy的根节点。它的onConfigurationChanged()属于深度优先搜索,向层级叶子节点回调onConfigurationChanged()

整体流程主要完成更新Window Tree Configuration、更新WindowToken(ActivityRecord)、WindowContainer(WindowState)的MergedConfiguration、更新Surface Position、更新Resources等工作。

经过RootWindowContainer.onConfiguraionChanged()完整流程后,系统内各个ConfiguraionContainerWindowContainerWindowToken均计算并应用了对应的Configuration了。

这个时候,仅仅是完成了WMS内部的Configuration更新,还没有完成应用侧的更新,以及绘制流程使用的Configuration之更新(ViewRootImpl)

Android11RootWindowContainer.onConfigurationChanged流程

WindowProcessController.onConfigurationChanged

ATMS.updateGlobalConfigurationLocked()RootWindowContainer完成整个Hierarchy的更新后,会回调系统全局所有WindowProcessControlleronConfigurationChanged()

ATMS.mProcessMap: WindowProcessControllerMap记录了所有正在运行的Android应用进程。

WindowProcessController.onConfigurationChanged()的流程相对RootWindowContainer比较简单些。它的主要工作是将新的Configuration通过ClientTransaction借助Binder发回ActivityThread,其中触发App侧的Resource更新、onConfiguraionChanged()的回调。

Android11WindowProcessController.onConfigurationChanged流程

DisplayContent.performDisplayOverrideConfigUpdate

紧接着,调用performDisplayOverrideConfigUpdate()来更新DisplayContent及Window Tree Hierarchy的DisplayInfoConfiguration,并作为Global Configuration设置到RootWindowContainer

Android11DisplayContent.performDisplayOverrideConfigUpdate流程

ATMS.ensureConfigAndVisibilityAfterUpdate

ATMS.updateGlobalConfiguraionLocked()完成后,紧接着DisplayContent.onDescendantOrientationChanged()会调用ATMS.ensureConfigAndVisibilityAfterUpdate()来检查所有Activity的可见性并更新它们的状态(包括Configuration、生命周期、可见性)。

AMTS.ensureConfigAndVisibilityAfterUpdate

ATMS.continueWindowLayout

当上面所有工作完成后,即可解除一开始就暂停的layout,调用ATMS.continueWindowLayout()将上面configuration changed后的变化应用起来,并继续窗口系统的绘制和显示。

在执行Traversal的过程中,对于Configuration改变导致的边界、大小发生变化的窗口即mResizingWindows内的窗口,会通过IWindow这个Binder回调到应用(ViewRootImpl.W),并最终回调到ViewRootImpl.dispatchResized()。该方法内部会更新传入的MergedOverrideConfiguration,并根据新的frame,通过setFrame()流程中的requestLayout()完成一次绘制,在应用界面上展现新的适配Configuration的画面。

Android11ATMS.continueWindowLayout流程

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来展示新的应用画面。

Android11ViewRootImpl.dispatchResized流程

总结

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。