Android 13 WindowContainer窗口模型详解
简介
Android 13后窗口模型系列架构存在较大变更,本文围绕WindowContainer总结窗口模型。
类图
窗口模型架构解析
新的窗口从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来显示应用画面。