简介

Android 13后窗口模型系列架构存在较大变更,本文围绕WindowContainer总结窗口模型。

类图

Android13窗口模型类图

窗口模型架构解析

新的窗口从WMS.addWindow()开始。首先根据DisplayId取得或创建对应的DisplayContent,然后创建WindowToken、WindowState,建立InputChannel,最后调用DisplayContent.sendNewConfiguration()。然后调用DisplayContent.updateOrientation()和sendNewConfiguration()来更新方向和conf。SurfaceControl由WindowToken管理。

DisplayContent.sendNewConfiguration()

如果addWindow()后方向发生改变,会触发对应Display的Conf更新。

根据Rotation和DisplayInfo,计算当前屏幕宽高、方向,组装Configuration和WindowConfiguration,将新的Configuraion通过DisplayContent.updateDisplayOverrideConfigurationLocked()更新下去(这个过程还在整个体系中确认Conf是否真的发生了update,如果发生了更新,最后才会设置DisplayContent.mLayoutNeeded)。

DisplayContent.updateDisplayOverrideConfigurationLocked()

更新当前Display对应的conf。对于Default Display更新Global Conf,否则更新override conf。

对于DEFAULT_DISPLAY,实际上不是仅更新Override Conf,而是直接更新Global Conf(过程中同步更新了Override Conf):ActivityTaskManagerService.updateGlobalConfigurationLocked()。

ActivityTaskManagerService.updateGlobalConfigurationLocked()

该方法实际上首先更新system_server,然后更新所有App,接着发出Global Conf Changed的广播,最后更新RootWindowContainer。

首先将system_server所在的进程更新到新的Configuration(通过ActivityThread.applyConfigurationToResources())。

WindowProcessController.onConfigurationChanged()

然后将新的Global Conf发给系统中所有应用进程:遍历ActivityTaskManagerService.mProcessMap,取得所有pid和对应的WindowProcessController。调用每个WindowProcessController.onConfigurationChanged(),内部使用ConfigurationChangeItem这个Binder,实现将Configuration changed发给Activity,触发Activity资源更新、回调Activity.onConfigurationChanged()。

广播ACTION_CONFIGURATION_CHANGED

接着,App Conf更新完成后,调度发出广播ACTION_CONFIGURATION_CHANGED:ActivityTaskManagerService向自己的Handler发送Message,回调broadcastGlobalConfigurationChanged(),发出广播。对于App而言,可以动态监听该广播(在Manifest静态注册无效)。

RootWindowContainer.onConfigurationChanged()

该方法更新系统中WindowContainer树,更新所有节点的Conf,包括ActivityRecord,实现应用新的Configuration。

ActivityTaskManagerService.ensureConfigAndVisibilityAfterUpdate()

上一步updateGlobalConfigrautionLocked()后,紧接着调用ensureConfigAndVisibilityAfterUpdate()。该方法实际上调用top ActivityRecord.ensureActivityConfiguration(),用于应用最新的Conf。

该步骤的目的是确定Activity是否需要更新到新的Conf(即判断新旧Conf是否一致)并进行更新,然后根据Activity注册的ConfigChanges属性,判断是否需要重启Activity还是回调对应方法。

此时ActivityRecord的Conf可能已经在上一步updateGlobalConfigurationLocked()发生变化了。

判断Config不变,直接调用scheduleConfigurationChanged()并返回。

判断Config有变(shouldRelaunchLocked()),可能提前返回/relaunchActivityLocked重启Activity,然后调用scheduleConfigurationChanged()。

ActivityRecord.scheduleConfigurationChanged()

通过Binder ActivityConfigurationChangeItem回调Activity.onConfigurationChanged()。

这里Binder是ActivityConfigurationChangeItem,而WindowProcessController是ConfigurationChangeItem。

小结

新的窗口添加或者切换后,如果方向发生变化,会更新Global Conf,并通过WindowProcessController触发回调所有App。在发出广播后,以RootWindowContainer为起点更新各个WindowContainer,最后将顶层Activity的conf再次更新,并触发一次新的绘制。

App

App会在WindowProcessController处通过ConfigurationChangeItem回调一次,在ActivityRecord处通过ActivityConfigurationChangeItem回调一次。

ConfigurationChangeItem

回调ActivityThread.handleConfigurationChanged():调用ConfigurationContainer.handleConfigurationChanged(),更新Resources,通过performConfigurationChanged()回调App内已有的各个onConfigurationChanged()回调。

ActivityConfigurationChangeItem

回调ActivityThread.handleActivityConfigurationChanged:调用performConfigurationChangedForActivity()->performActivityConfigurationChanged():如果Conf的窗口模式变化时触发对应回调;判断当Conf和Resources有public域发生变化时,调用ResourcesManager.updateResourcesForActivity()更新Resources,回调Activity.onConfigurationChanged()。

然后,调用ViewRootImpl.updateConfiguration()。

ViewRootImpl.updateConfiguration()

调用mView.dispatchConfigurationChanged(),从DecorView开始回调各View的onConfigurationChanged()。

最后,调用requestLayout():向ChoreoGrapher注册回调,在下一次屏幕刷新中回调performTraversals()。该方法开始按照新的Configuration执行绘制工作(新的Configuration在上一步即ResourcesManager.updateResources()中更新了,ViewRootImpl.performTraversal()也是使用这个Resources.getConfiguration()来绘制的)。

小结

ActivityThread在Global Conf对应的从WindowProcessController的回调中,会将新的Configuration通过ConfigurationController进行更新,并回调各个onConfigurationChanged()。

窗口体系Configuration更新完成后,通过ActivityRecord会再次更新顶层Activity,此时更新的override conf可能和Global Conf并不一致。接着触发一次View Tree的重新绘制,按照新的Conf来显示应用画面。