4【Android 12】ConfigurationContainer类

4【Android 12】ConfigurationContainer类_第1张图片

之前在分析WMS的窗口层级结构时,看到WindowContainer的定义为:

class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<E>
        implements Comparable<WindowContainer>, Animatable, SurfaceFreezer.Freezable {

看到WindowContainer其实还是有一个父类ConfigurationContainer的,但是由于ConfigurationContainer并不参与层级结构的构建,所以当时并没有对ConfigurationContainer进行介绍,本篇就详细分析一下这个类的作用。

1 ConfigurationContainer类

首先ConfigurationContainer的定义为:

/**
 * Contains common logic for classes that have override configurations and are organized in a
 * hierarchy.
 */
public abstract class ConfigurationContainer<E extends ConfigurationContainer> {

包含具有覆盖configuration并以层次结构组织的类的通用逻辑。

从名字我们可以知道ConfigurationContainer是一个container,类似WindowContainer的容器,不过这个容器盛放的是Configuration对象,而WindowContainer是窗口的容器。

ConfigurationContainer只有两个直接子类,WindowContainer和WindowProcessController,WindowContainer之前详细介绍过自不必多说,而WindowProcesController则保存了一些进程相关的信息,是WM和AM沟通的媒介,和本文要分析的内容也没太大关系,因此继承关系这部分可以暂时忽略。

如上面所说ConfigurationContainer虽然没有参与层级结构的构建,但是它却借助了WindowContainer构建起来的层级结构,从上到下进行了进行Configuration的分发,那么本篇的重点并不在于层级结构,而是Configuration。

先来简单介绍一下Configuration的相关内容,方便我们后续分析。

2 Configuration类

Configuration类的定义为:

/**
 * This class describes all device configuration information that can
 * impact the resources the application retrieves.  This includes both
 * user-specified configuration options (locale list and scaling) as well
 * as device configurations (such as input modes, screen size and screen orientation).
 * 

You can acquire this object from {@link Resources}, using {@link * Resources#getConfiguration}. Thus, from an activity, you can get it by chaining the request * with {@link android.app.Activity#getResources}:

*
Configuration config = getResources().getConfiguration();
*/
public final class Configuration implements Parcelable, Comparable<Configuration> {

这个类描述了所有可能影响应用程序检索的资源的设备配置信息。这包括用户指定的配置选项(语言区域列表和缩放)以及设备的配置(如输入模式、屏幕大小和屏幕方向)。

这个类不管是做App还是Framework的都会经常接触,重要性不言而喻。它的每个成员变量单拎出来都是重量级,这里并不一一介绍,作为WMS相关的开发,我平时关注比较多的则是代表方向的orientation,以及代表屏幕尺寸的screenWidthDp和screenHeightDp等,当然这些大家都熟悉的东西也不是本篇重点,重点是Configuration的成员变量windowConfiguration。

    /**
     * Configuration relating to the windowing state of the object associated with this
     * Configuration. Contents of this field are not intended to affect resources, but need to be
     * communicated and propagated at the same time as the rest of Configuration.
     * @hide
     */
    @TestApi
    public final WindowConfiguration windowConfiguration = new WindowConfiguration();

WindowConfiguration持有了窗口状态相关的配置信息,这些信息对于App开发者来说基本用不到,但是仍然要作为Configuration的一部分,跟随Configuration一起进行分发。

3 WindowConfiguration类

/**
 * Class that contains windowing configuration/state for other objects that contain windows directly
 * or indirectly. E.g. Activities, Task, Displays, ...
 * The test class is {@link com.android.server.wm.WindowConfigurationTests} which must be kept
 * up-to-date and ran anytime changes are made to this class.
 * @hide
 */
@TestApi
public class WindowConfiguration implements Parcelable, Comparable<WindowConfiguration>

包含窗口配置或状态信息的类,用于那些直接或间接包含窗口的容器。

3.1 mBounds、mAppBounds和mMaxBounds

    /**
     * bounds that can differ from app bounds, which may include things such as insets.
     *
     * TODO: Investigate combining with {@link #mAppBounds}. Can the latter be a product of the
     * former?
     */
    private final Rect mBounds = new Rect();

    /**
     * {@link android.graphics.Rect} defining app bounds. The dimensions override usages of
     * {@link DisplayInfo#appHeight} and {@link DisplayInfo#appWidth} and mirrors these values at
     * the display level. Lower levels can override these values to provide custom bounds to enforce
     * features such as a max aspect ratio.
     */
    private Rect mAppBounds;

    /**
     * The maximum {@link Rect} bounds that an app can expect. It is used to report value of
     * {@link WindowManager#getMaximumWindowMetrics()}.
     */
    private final Rect mMaxBounds = new Rect();
  • mBounds,这个是最常用的bounds,代表了container的边界。

    各种container,比如Task,调用getBounds方法,返回的就是这个bounds,这个bounds代表的也就是Task的边界:

        /**
         * Returns the effective bounds of this container, inheriting the first non-empty bounds set in
         * its ancestral hierarchy, including itself.
         */
        public Rect getBounds() {
            mReturnBounds.set(getConfiguration().windowConfiguration.getBounds());
            return mReturnBounds;
        }
    
  • mAppBounds,用到的地方不多,代表的是App的可用边界,对比mBounds,一个很明显的区别就是mAppBounds的计算是考虑到了insets的,而mBounds的边界是不考虑insets的,比如通过adb shell dumpsys activity a打印出的全屏状态下的某个ActivityRecord的Configuration:

    CurrentConfiguration={0.93 ?mcc?mnc [zh_CN_#Hans] ldltr sw360dp w360dp h736dp 320dpi nrml long port finger -keyb/v/h -nav/h winConfig={ mBounds=Rect(0, 0 - 720, 1612) mAppBounds=Rect(0, 44 - 720, 1516) mMaxBounds=Rect(0, 0 - 720, 1612) mWindowingMode=fullscreen mDisplayWindowingMode=fullscreen mActivityType=standard mAlwaysOnTop=undefined mRotation=ROTATION_0} as.6 s.1 fontWeightAdjustment=0}
    

    这里的mBounds为:

    mBounds=Rect(0, 0 - 720, 1612)
    

    即整个屏幕的大小,而mAppBounds为:

    mAppBounds=Rect(0, 44 - 720, 1516)
    

    明显是减去了状态栏和导航栏的高度。

    mAppBounds一个比较重要的作用是,计算Configuration的screenWidthDp和screenHeightDp,比如还是以上的Configuration,它的screenWidthDp和screenHeightDp是:

    w360dp h736dp
    

    再结合它的densityDpi:

    320dpi
    

    很明显,screenWidthDp和screenHeightDp是根据mAppBounds计算的,而不是mBounds,而实际上也的确是这样的,具体计算过程在Task.computeConfigResourceOverrides方法中,这里就不详细分析了。

  • mMaxBounds,代表了一个container能够得到的最大bounds,比如分屏下,一个ActivityRecord的各个bounds可能是:

    mBounds=Rect(0, 0 - 720, 770) mAppBounds=Rect(0, 44 - 720, 770) mMaxBounds=Rect(0, 0 - 720, 1612)
    

    mBounds代表的是当前ActivityRecord的大小,mAppBounds在mBounds的基础上减去的状态栏的高度,而mMaxBounds代表的则是这个ActivityRecord可以拥有的最大尺寸,即全屏。

    mMaxBounds的出现应该是在Display.getRealSize被弃用,改为通过WindowMetrics获取屏幕尺寸时候。App如果想要获取当前activity的边界,应该使用WindowManager.getCurrentWindowMetrics,如果想要知道App能够占据的最大边界,那么应该使用WindowManager.getMaximumWindowMetrics。

    大部分时候,mMaxBounds代表的都是屏幕的尺寸,但是少部分特殊模式下,它可能会小于屏幕的实际物理尺寸。

3.2 mRotation

    /**
     * The current rotation of this window container relative to the default
     * orientation of the display it is on (regardless of how deep in the hierarchy
     * it is). It is used by the configuration hierarchy to apply rotation-dependent
     * policy during bounds calculation.
     */
    private int mRotation = ROTATION_UNDEFINED;

当前container的旋转角度,只和当前container所在的display相关,和这个container在层级结构中的哪一级无关。

一般来说,各级container都是直接继承Display的rotation的,但是ActivityRecord中针对尺寸兼容模式有了额外的逻辑,这部分看的比较少,暂时略过。

3.3 mWindowingMode和mDisplayWindowingMode

    /** The current windowing mode of the configuration. */
    private @WindowingMode int mWindowingMode;

    /** The display windowing mode of the configuration */
    private @WindowingMode int mDisplayWindowingMode;
  • mWindowingMode,代表了当前container处于哪一种多窗口模式里。
  • mDisplayWindowingMode,代表了当前container所在的Display处于哪一种多窗口模式里。

目前的多窗口模式有以下几种:

    /** Windowing mode is currently not defined. */
    public static final int WINDOWING_MODE_UNDEFINED = 0;
    /** Occupies the full area of the screen or the parent container. */
    public static final int WINDOWING_MODE_FULLSCREEN = 1;
    /** Always on-top (always visible). of other siblings in its parent container. */
    public static final int WINDOWING_MODE_PINNED = 2;
    /** The primary container driving the screen to be in split-screen mode. */
    // TODO: Remove once split-screen is migrated to wm-shell.
    public static final int WINDOWING_MODE_SPLIT_SCREEN_PRIMARY = 3;
    /**
     * The containers adjacent to the {@link #WINDOWING_MODE_SPLIT_SCREEN_PRIMARY} container in
     * split-screen mode.
     * NOTE: Containers launched with the windowing mode with APIs like
     * {@link ActivityOptions#setLaunchWindowingMode(int)} will be launched in
     * {@link #WINDOWING_MODE_FULLSCREEN} if the display isn't currently in split-screen windowing
     * mode
     */
    // TODO: Remove once split-screen is migrated to wm-shell.
    public static final int WINDOWING_MODE_SPLIT_SCREEN_SECONDARY = 4;
    /**
     * Alias for {@link #WINDOWING_MODE_SPLIT_SCREEN_SECONDARY} that makes it clear that the usage
     * points for APIs like {@link ActivityOptions#setLaunchWindowingMode(int)} that the container
     * will launch into fullscreen or split-screen secondary depending on if the device is currently
     * in fullscreen mode or split-screen mode.
     */
    // TODO: Remove once split-screen is migrated to wm-shell.
    public static final int WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY =
            WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
    /** Can be freely resized within its parent container. */
    // TODO: Remove once freeform is migrated to wm-shell.
    public static final int WINDOWING_MODE_FREEFORM = 5;
    /** Generic multi-window with no presentation attribution from the window manager. */
    public static final int WINDOWING_MODE_MULTI_WINDOW = 6;
  • WINDOWING_MODE_UNDEFINED,未定义,一般container刚刚创建的时候就是这个模式,但是后续必须要为其选择一种非WINDOWING_MODE_UNDEFINED的窗口模式。

  • WINDOWING_MODE_FULLSCREEN,全屏模式,占据了整个屏幕空间,很常见就不讨论了。

  • WINDOWING_MODE_PINNED,画中画模式,App中的Activity能够以一个小窗口的形式启动,并且可以在其他App的上方显示,常见于视频播放。

  • WINDOWING_MODE_SPLIT_SCREEN_PRIMARY和WINDOWING_MODE_SPLIT_SCREEN_SECONDARY,分屏模式,并排显示两个App,primary和secondary分别代表了左右或上下半屏的App所处的窗口模式。

  • WINDOWING_MODE_FREEFORM,自由窗口模式,不同于PIP,这个模式下整个App都位于小窗口模式。

  • WINDOWING_MODE_MULTI_WINDOW,Android13的分屏模式对应的窗口模式,Android12应该没有用到的地方。

窗口模式主要是为多窗口模式设计,在进入多窗口模式前,container都是fullscreen模式。当把某个App对应的Task移动到多窗口模式,如freeform时,该Task对应的Configuration.windowConfiguration.mWindowingMode会被置为WINDOWING_MODE_FREEFORM,Task中的子container也会继承Task的这一属性,即Task中的ActivityRecord,ActivityRecord的WindowState等,它们对应的Configuration.windowConfiguration.mWindowingMode全会被置为WINDOWING_MODE_FREEFORM。

3.4 mActivityType

    /** The current activity type of the configuration. */
    private @ActivityType int mActivityType;

当前container的activity类型。

activity类型有以下几种:

    /** Activity type is currently not defined. */
    public static final int ACTIVITY_TYPE_UNDEFINED = 0;
    /** Standard activity type. Nothing special about the activity... */
    public static final int ACTIVITY_TYPE_STANDARD = 1;
    /** Home/Launcher activity type. */
    public static final int ACTIVITY_TYPE_HOME = 2;
    /** Recents/Overview activity type. There is only one activity with this type in the system. */
    public static final int ACTIVITY_TYPE_RECENTS = 3;
    /** Assistant activity type. */
    public static final int ACTIVITY_TYPE_ASSISTANT = 4;
    /** Dream activity type. */
    public static final int ACTIVITY_TYPE_DREAM = 5;
  • ACTIVITY_TYPE_UNDEFINED,一般container刚刚创建的时候就是这个模式,但是后续必须要为其选择一种非ACTIVITY_TYPE_UNDEFINED的activity类型。
  • ACTIVITY_TYPE_STANDARD,标准类型,大部分的App都是这种类型。
  • ACTIVITY_TYPE_HOME,Launcher类型的App,如google原生Launcher,Launcher3的ActivityRecord都是这个类型。
  • ACTIVITY_TYPE_RECENTS,Recents或Overview类型,系统中只有一个activity是这个类型的,即”com.android.launcher3/com.android.quickstep.RecentsActivity“。
  • ACTIVITY_TYPE_ASSISTANT,Assistant类型,目前接触的不多,应该和google语音助手服务"voice_interaction_service"之类的相关。
  • ACTIVITY_TYPE_DREAM,Dream类型,完全没有遇到过,目前应该只有DreamActivity对应的ActivityRecord能够被设置为此类型。

具体的Activity类型的计算在ActivityRecord.setActivityType中。

ActivityType和WindowingMode有一点不同的是,在将某个App移动到多窗口模式时,是先设置Task的WindowingMode,Task中的子container继承Task的WindowingMode。而Task的ActivityType则是由Task中的ActivityRecord的ActivityType类型决定,ActivityRecord在其构造方法中会去调用ActivityRecord.setActivityType计算ActivityRecord的类型,随后ActivityRecord在添加到Task中时,Task将自己的ActivityType设置为ActivityRecord的类型。

将container按照ActivityType进行分类,还是为了便于管理和使用container,之前在分析WindowContainer类的时候就看到,HOME类型的Task是支持嵌套的,所有HOME类型的Task会统一放到一个HOME类型的Task中去管理,比如开机的时候的默认Launcher是Launcher1,那么系统会首先创建一个HOME类型的Task#1,作为HOME类型Task的root,接着为Launcher1创建Task#2用来存放Launcher1的activity,并且把Task#2作为子container添加到Task#1中。后续如果我们把默认Launcher切换为Launcher2,那么此时系统会再创建一个Task#3,作为Launcher2的Task,此时Task#3也会被作为Task#1的子container添加到Task#3中,和Task#2处于同一层级。

对于特殊窗口类型和Activity类型的root Task,作为Task容器的TaskDisplayArea,也创建了一些专门的成员变量来存放它们的引用:

    // Cached reference to some special tasks we tend to get a lot so we don't need to loop
    // through the list to find them.
    private Task mRootHomeTask;
    private Task mRootPinnedTask;
    private Task mRootSplitScreenPrimaryTask;

    // TODO(b/159029784): Remove when getStack() behavior is cleaned-up
    private Task mRootRecentsTask;

这些Task的特点之一是同一时间只能存在一个,如果mRootHomeTask不为空的情况下再去添加一个HOME类型的root Task,就会抛出异常。

3.5 mAlwaysOnTop

    /** The current always on top status of the configuration. */
    private @AlwaysOnTop int mAlwaysOnTop;

mAlwaysOnTop用来声明当前container是否总是处于顶层。

有以下几个值:

    /** Always on top is currently not defined. */
    private static final int ALWAYS_ON_TOP_UNDEFINED = 0;
    /** Always on top is currently on for this configuration. */
    private static final int ALWAYS_ON_TOP_ON = 1;
    /** Always on top is currently off for this configuration. */
    private static final int ALWAYS_ON_TOP_OFF = 2;

比较简单不再赘述,再看下有哪些类型的container是可以总是处于顶层的:

    /**
     * Returns true if the container associated with this window configuration is always-on-top of
     * its siblings.
     * @hide
     */
    public boolean isAlwaysOnTop() {
        if (mWindowingMode == WINDOWING_MODE_PINNED) return true;
        if (mActivityType == ACTIVITY_TYPE_DREAM) return true;
        if (mAlwaysOnTop != ALWAYS_ON_TOP_ON) return false;
        return mWindowingMode == WINDOWING_MODE_FREEFORM
                    || mWindowingMode == WINDOWING_MODE_MULTI_WINDOW;
    }

对于PIP模式和DREAM类型的container,是需要用于位于顶层的,其他的container,即使设置了mAlwaysOnTop为ALWAYS_ON_TOP_ON,也需要是WINDOWING_MODE_FREEFORM或WINDOWING_MODE_MULTI_WINDOW类型才行。

至于这个”总是位于顶层“,也不是将其置于所有container之上,而是置于与它处于同一层级的container之上,比如一个PIP模式的Task,只可能位于TaskDisplayArea的顶层,即其他Task之上,而不可能超出TaskDisplayArea,位于StatusBar的上面。

至此,Configuration类和WindowConfiguration类的相关知识介绍的差不多了,可以进行下一步的ConfigurationContainer的介绍了。

4 ConfigurationContainer成员变量

ConfigurationContainer既然是Configuration的容器,那么它肯定是存储了一些Configuration了,都有哪些呢?

ConfigurationContainer的成员变量也就那几个,不难看到和Configuration相关的有mRequestedOverrideConfiguration、mResolvedOverrideConfiguration、mFullConfiguration和mMergedOverrideConfiguration。

  • mRequestedOverrideConfiguration

        /**
         * Contains requested override configuration settings applied to this configuration container.
         */
        private Configuration mRequestedOverrideConfiguration = new Configuration();
    

    包含了应用到当前container的请求的覆盖Configuration。

  • mResolvedOverrideConfiguration

        /**
         * Contains the requested override configuration with parent and policy constraints applied.
         * This is the set of overrides that gets applied to the full and merged configurations.
         */
        private Configuration mResolvedOverrideConfiguration = new Configuration();
    

    包含了应用了父容器和策略限制下的请求的覆盖Configuration。这是应用到mFullConfiguration和mMergedOverrideConfiguration的覆盖Configuration集合。

  • mFullConfiguration

        /**
         * Contains full configuration applied to this configuration container. Corresponds to full
         * parent's config with applied {@link #mResolvedOverrideConfiguration}.
         */
        private Configuration mFullConfiguration = new Configuration();
    

    包含了应用到此container的完整Configuration。相当于父容器的Configuration加上mResolvedOverrideConfiguration。

  • mMergedOverrideConfiguration

    这个成员变量放在最后一节分析。

有点抽象,还是从它们作用的地方入手,看下这些个成员的作用都是什么。

4.1 例子:将Task设置为自由窗口模式

直接拿一个具体的例子看下。

假如我想将一个Task设置为自由窗口模式,那么我期望的最终结果应该是,这个Task持有的mFullConfiguration成员变量中的WindowingMode变为WINDOWING_MODE_FREEFORM,而我这边可能的做法则是拿到这个Task之后,调用系统已经支持的ConfigurationContainer.setWindowingMode方法来设置这个Task的WindowingMode为WINDOWING_MODE_FREEFORM,如ActivityClientController.toggleFreeformWindowingMode方法的做法:

    @Override
    public void toggleFreeformWindowingMode(IBinder token) {
        final long ident = Binder.clearCallingIdentity();
        try {
            synchronized (mGlobalLock) {
                // ......

                if (rootTask.inFreeformWindowingMode()) {
                    // ......
                } else {
                    rootTask.setWindowingMode(WINDOWING_MODE_FREEFORM);
                }
            }
        } finally {
            Binder.restoreCallingIdentity(ident);
        }
    }

这里的rootTask是我想移动到自由窗口模式的那个Task,这里为该Task调用了setWindowingMode方法,忽略中间的过程,这最终会调用到ConfigurationContainer的setWindowingMode方法。

首先看一下setWindowingMode方法:

    /** Sets the requested windowing mode override for the configuration container. */
    public void setWindowingMode(/*@WindowConfiguration.WindowingMode*/ int windowingMode) {
        mRequestsTmpConfig.setTo(getRequestedOverrideConfiguration());
        mRequestsTmpConfig.windowConfiguration.setWindowingMode(windowingMode);
        onRequestedOverrideConfigurationChanged(mRequestsTmpConfig);
    }

这一步也很简单:

1)、首先调用getRequestedOverrideConfiguration方法:

    /** Returns requested override configuration applied to this configuration container. */
    public Configuration getRequestedOverrideConfiguration() {
        return mRequestedOverrideConfiguration;
    }

将当前Task的mRequestedOverrideConfiguration的值赋值给一个临时的变量mRequestsTmpConfig,毕竟不可能说每次设置一个新的WindowingMode的时候就把之前请求的一些配置信息全部都抛弃了,肯定要在之前已经请求的信息的基础上修改。

2)、接着将请求的这个WindowingMode先保存在mRequestsTmpConfig中。

3)、最后调用onRequestedOverrideConfigurationChanged方法,传入的则是携带了新请求的WindowingMode信息的mRequestsTmpConfig。

4.2 mRequestedOverrideConfiguration成员变量和onRequestedOverrideConfigurationChanged成员方法

ConfigurationContainer的onRequestedOverrideConfigurationChanged方法的内容为:

    /**
     * Update override configuration and recalculate full config.
     * @see #mRequestedOverrideConfiguration
     * @see #mFullConfiguration
     */
    public void onRequestedOverrideConfigurationChanged(Configuration overrideConfiguration) {
        // Pre-compute this here, so we don't need to go through the entire Configuration when
        // writing to proto (which has significant cost if we write a lot of empty configurations).
        mHasOverrideConfiguration = !Configuration.EMPTY.equals(overrideConfiguration);
        mRequestedOverrideConfiguration.setTo(overrideConfiguration);
        // ......
        final ConfigurationContainer parent = getParent();
        onConfigurationChanged(parent != null ? parent.getConfiguration() : Configuration.EMPTY);
    }

过滤不重要的因素,首先看到:

1)、mHasOverrideConfiguration表明了当前container是否请求了一些特殊的配置,至于请求的配置自然是存放在mRequestedOverrideConfiguration中:

        mHasOverrideConfiguration = !Configuration.EMPTY.equals(overrideConfiguration);
        mRequestedOverrideConfiguration.setTo(overrideConfiguration);

2)、自从ActivityClientController.toggleFreeformWindowingMode方法中我们调用:

                    rootTask.setWindowingMode(WINDOWING_MODE_FREEFORM);

后,至此WINDOWING_MODE_FREEFORM似乎只是保存到了mRequestedOverrideConfiguration中了,似乎并没有生效,那什么时候生效呢,答案在onRequestedOverrideConfigurationChanged方法中的最后一句:

        onConfigurationChanged(parent != null ? parent.getConfiguration() : Configuration.EMPTY);

onConfigurationChanged方法是我们接下来分析的重点。

另外注意,这里onConfigurationChanged方法传入的并不是mRequestedOverrideConfiguration,而是parent.getConfiguration,即父容器的mFullConfiguration。

这里可能会有疑问,为啥在ConfigurationContainer.setWindowingMode中,还要先用一个临时变量mRequestsTmpConfig来存放这个请求的WindowingMode呢,直接把这个WindowingMode设置到mRequestedOverrideConfiguration里不好吗?这个是因为,有的ConfigurationContainer的子类可能会重写onRequestedOverrideConfigurationChanged方法,如DisplayContent:

    @Override
    public void onRequestedOverrideConfigurationChanged(Configuration overrideConfiguration) {
        final Configuration currOverrideConfig = getRequestedOverrideConfiguration();
        final int currRotation = currOverrideConfig.windowConfiguration.getRotation();
        final int overrideRotation = overrideConfiguration.windowConfiguration.getRotation();
        if (currRotation != ROTATION_UNDEFINED && currRotation != overrideRotation) {
            applyRotationAndFinishFixedRotation(currRotation, overrideRotation);
        }
        mCurrentOverrideConfigurationChanges = currOverrideConfig.diff(overrideConfiguration);
        super.onRequestedOverrideConfigurationChanged(overrideConfiguration);
        mCurrentOverrideConfigurationChanges = 0;
        mWmService.setNewDisplayOverrideConfiguration(currOverrideConfig, this);
        mAtmService.addWindowLayoutReasons(
                ActivityTaskManagerService.LAYOUT_REASON_CONFIG_CHANGED);
    }

在这些重写的onRequestedOverrideConfigurationChanged方法中,有些子类可能希望在请求的配置生效之前,获取到请求前的配置信息,来和请求后的配置信息进行对比,如这里的:

        final Configuration currOverrideConfig = getRequestedOverrideConfiguration();
        final int currRotation = currOverrideConfig.windowConfiguration.getRotation();
        final int overrideRotation = overrideConfiguration.windowConfiguration.getRotation();
        if (currRotation != ROTATION_UNDEFINED && currRotation != overrideRotation) {
            applyRotationAndFinishFixedRotation(currRotation, overrideRotation);
        }

因此ConfigurationContainer的各个子类去重写onRequestedOverrideConfigurationChanged方法是允许的,但是别忘了调用:

super.onRequestedOverrideConfigurationChanged(overrideConfiguration);

确保本次请求的配置信息能够保存到mRequestedOverrideConfiguration中,以及ConfigurationContainer.onConfigurationChanged方法接下来能够被调用。

在分析ConfigurationContainer.onConfigurationChanged方法之前,小结一下mRequestedOverrideConfiguration和onRequestedOverrideConfigurationChanged的作用。

除了上面所举例子中调用的ConfigurationContainer.setWindowingMode,其他的对WindowConfiguration的更改,如setActivityType:

    /** Sets the activity type to associate with the configuration container. */
    public void setActivityType(/*@WindowConfiguration.ActivityType*/ int activityType) {
        int currentActivityType = getActivityType();
        if (currentActivityType == activityType) {
            return;
        }
        if (currentActivityType != ACTIVITY_TYPE_UNDEFINED) {
            throw new IllegalStateException("Can't change activity type once set: " + this
                    + " activityType=" + activityTypeToString(activityType));
        }
        mRequestsTmpConfig.setTo(getRequestedOverrideConfiguration());
        mRequestsTmpConfig.windowConfiguration.setActivityType(activityType);
        onRequestedOverrideConfigurationChanged(mRequestsTmpConfig);
    }

以及setBounds:

    public int setBounds(Rect bounds) {
        int boundsChange = diffRequestedOverrideBounds(bounds);
        final boolean overrideMaxBounds = providesMaxBounds()
                && diffRequestedOverrideMaxBounds(bounds) != BOUNDS_CHANGE_NONE;

        if (boundsChange == BOUNDS_CHANGE_NONE && !overrideMaxBounds) {
            return boundsChange;
        }

        mRequestsTmpConfig.setTo(getRequestedOverrideConfiguration());
        mRequestsTmpConfig.windowConfiguration.setBounds(bounds);
        onRequestedOverrideConfigurationChanged(mRequestsTmpConfig);

        return boundsChange;
    }

以及各类setXXX方法:

4【Android 12】ConfigurationContainer类_第2张图片

可以看出,ConfigurationContainer下的这类setXXX方法的逻辑都是类似的:

1)、先将请求的信息保存在了临时变量mRequestsTmpConfig中。

2)、接着调用onRequestedOverrideConfigurationChanged方法,传入mRequestsTmpConfig。

所以这类setXXX方法的关键在于onRequestedOverrideConfigurationChanged方法的调用。

在onRequestedOverrideConfigurationChanged方法中,重要的两个步骤则是:

1)、将请求的配置信息保存到mRequestedOverrideConfiguration中。

2)、调用onConfigurationChanged方法。

那么mRequestedOverrideConfiguration成员变量的作用,则是保存当前container主动为自己请求的一些特有的信息,目的是防止这部分的信息后续不会随着Configuration的更新而丢失。

而onRequestedOverrideConfigurationChanged方法虽然支持重写,但是最好在重写方法中调用:

super.onRequestedOverrideConfigurationChanged(overrideConfiguration);

确保本次请求的配置信息能够保存到mRequestedOverrideConfiguration中,以及ConfigurationContainer.onConfigurationChanged方法接下来能够被调用。

4.3 mFullConfiguration成员变量和onConfigurationChanged成员方法

    /**
     * Notify that parent config changed and we need to update full configuration.
     * @see #mFullConfiguration
     */
    public void onConfigurationChanged(Configuration newParentConfig) {
        mResolvedTmpConfig.setTo(mResolvedOverrideConfiguration);
        resolveOverrideConfiguration(newParentConfig);
        mFullConfiguration.setTo(newParentConfig);
        mFullConfiguration.updateFrom(mResolvedOverrideConfiguration);
        onMergedOverrideConfigurationChanged();
        if (!mResolvedTmpConfig.equals(mResolvedOverrideConfiguration)) {
            // This depends on the assumption that change-listeners don't do
            // their own override resolution. This way, dependent hierarchies
            // can stay properly synced-up with a primary hierarchy's constraints.
            // Since the hierarchies will be merged, this whole thing will go away
            // before the assumption will be broken.
            // Inform listeners of the change.
            for (int i = mChangeListeners.size() - 1; i >= 0; --i) {
                mChangeListeners.get(i).onRequestedOverrideConfigurationChanged(
                        mResolvedOverrideConfiguration);
            }
        }
        for (int i = mChangeListeners.size() - 1; i >= 0; --i) {
            mChangeListeners.get(i).onMergedOverrideConfigurationChanged(
                    mMergedOverrideConfiguration);
        }
        for (int i = getChildCount() - 1; i >= 0; --i) {
            dispatchConfigurationToChild(getChildAt(i), mFullConfiguration);
        }
    }

1)、首先将mResolvedOverrideConfiguration的值赋值给mResolvedTmpConfig:

        mResolvedTmpConfig.setTo(mResolvedOverrideConfiguration);

2)、接着重新计算mResolvedOverrideConfiguration的值:

        resolveOverrideConfiguration(newParentConfig);

看下ConfigurationContainer.resolveOverrideConfiguration方法:

    /**
     * Resolves the current requested override configuration into
     * {@link #mResolvedOverrideConfiguration}
     *
     * @param newParentConfig The new parent configuration to resolve overrides against.
     */
    void resolveOverrideConfiguration(Configuration newParentConfig) {
        mResolvedOverrideConfiguration.setTo(mRequestedOverrideConfiguration);
    }

很简单,将mRequestedOverrideConfiguration的值赋值给mResolvedOverrideConfiguration,这就是为什么mRequestedOverrideConfiguration的值可以影响最终的mFullConfiguration。

3)、将父容器的mFullConfiguration赋值给当前容器的mFullConfiguration:

        mFullConfiguration.setTo(newParentConfig);

在之前的分析中我们也知道了,这里的newParentConfig,即parent.getConfiguration,也就是父容器的mFullConfiguration。

这一步很重要,这也说明了并不需要在每个container的onConfigurationChanged方法中,都重新计算一次新的Configuraiton中的每个成员变量,大部分时候,子container都是直接继承父container的Configuration。这也很好理解,比如这里我设置了某个Task的WindowingMode为WINDOWING_MODE_FREEFORM,那么就没有必要再为这个Task中的每一个ActivityRecord,都显式地进行一次如下操作:

activityRecord.getConfiguration().setWindowingMode(WINDOWING_MODE_FREEFORM);

4)、用mResolvedOverrideConfiguration对将当前container的mFullConfiguration进行更新:

        mFullConfiguration.updateFrom(mResolvedOverrideConfiguration);

至此,这次对Task的WindowingMode的设置才算生效,从ConfigurationContainer.onConfigurationChanged的代码执行顺序我们也能看出,mFullConfiguration才是能够代表当前container Configuration的那个。

这一点从ConfigurationContainer的各种返回WindowConfiguration信息的方法也能看出来,比如我们调用Task.getActivityType,那么实际调用的是ConfigurationContainer.getActivityType:

    /** Returns the activity type associated with the the configuration container. */
    /*@WindowConfiguration.ActivityType*/
    public int getActivityType() {
        return mFullConfiguration.windowConfiguration.getActivityType();
    }

返回的则是mFullConfiguration的ActivityType,而不是mRequestedOverrideConfiguration或者mResolvedOverrideConfiguration的。

4.4 mResolvedOverrideConfiguration成员变量和resolveOverrideConfiguration成员方法

上面的分析中并没有体现mResolvedOverrideConfiguration的作用,并且ConfigurationContainer.resolveOverrideConfiguration方法的内容也很奇怪:

    /**
     * Resolves the current requested override configuration into
     * {@link #mResolvedOverrideConfiguration}
     *
     * @param newParentConfig The new parent configuration to resolve overrides against.
     */
    void resolveOverrideConfiguration(Configuration newParentConfig) {
        mResolvedOverrideConfiguration.setTo(mRequestedOverrideConfiguration);
    }

只是将mResolvedOverrideConfiguration的值赋值为mRequestedOverrideConfiguration,传入的newParentConfig却压根没有用到。

其实这个方法,比起onRequestedOverrideConfigurationChanged方法,更是需要被ConfigurationContainer的子类重写,才能发挥mResolvedOverrideConfiguration的作用,目前重写该方法的子类有:

4【Android 12】ConfigurationContainer类_第3张图片

拿其中一个看一下,Task的resolveOverrideConfiguration方法:

    @Override
    void resolveOverrideConfiguration(Configuration newParentConfig) {
        mTmpBounds.set(getResolvedOverrideConfiguration().windowConfiguration.getBounds());
        super.resolveOverrideConfiguration(newParentConfig);

        int windowingMode =
                getResolvedOverrideConfiguration().windowConfiguration.getWindowingMode();
        final int parentWindowingMode = newParentConfig.windowConfiguration.getWindowingMode();

        // Resolve override windowing mode to fullscreen for home task (even on freeform
        // display), or split-screen if in split-screen mode.
        if (getActivityType() == ACTIVITY_TYPE_HOME && windowingMode == WINDOWING_MODE_UNDEFINED) {
            windowingMode = WindowConfiguration.isSplitScreenWindowingMode(parentWindowingMode)
                    ? parentWindowingMode : WINDOWING_MODE_FULLSCREEN;
            getResolvedOverrideConfiguration().windowConfiguration.setWindowingMode(windowingMode);
        }

        // Do not allow tasks not support multi window to be in a multi-window mode, unless it is in
        // pinned windowing mode.
        if (!supportsMultiWindow()) {
            final int candidateWindowingMode =
                    windowingMode != WINDOWING_MODE_UNDEFINED ? windowingMode : parentWindowingMode;
            if (WindowConfiguration.inMultiWindowMode(candidateWindowingMode)
                    && candidateWindowingMode != WINDOWING_MODE_PINNED) {
                getResolvedOverrideConfiguration().windowConfiguration.setWindowingMode(
                        WINDOWING_MODE_FULLSCREEN);
            }
        }

        if (isLeafTask()) {
            resolveLeafOnlyOverrideConfigs(newParentConfig, mTmpBounds /* previousBounds */);
        }
        computeConfigResourceOverrides(getResolvedOverrideConfiguration(), newParentConfig);
    }

1)、首先调用父类的resolveOverrideConfiguration,将mRequestedOverrideConfiguration的值赋值给mResolvedOverrideConfiguration:

        super.resolveOverrideConfiguration(newParentConfig);

mRequestedOverrideConfiguration保存的是当前container主动请求的特有信息,我们要在此基础上进行策略约束的应用。

2)、这里算是强制规定HOME类型的Task的WindowingMode只能为分屏类型或者全屏类型:

        // Resolve override windowing mode to fullscreen for home task (even on freeform
        // display), or split-screen if in split-screen mode.
        if (getActivityType() == ACTIVITY_TYPE_HOME && windowingMode == WINDOWING_MODE_UNDEFINED) {
            windowingMode = WindowConfiguration.isSplitScreenWindowingMode(parentWindowingMode)
                    ? parentWindowingMode : WINDOWING_MODE_FULLSCREEN;
            getResolvedOverrideConfiguration().windowConfiguration.setWindowingMode(windowingMode);
        }

这一条便是针对HOME类型的Task进行的策略约束,确保HOME类型的Task不被设置为异常的WindowingMode。

3)、如果Task不支持多窗口模式,那么即使强制将这个Task的WindowingMode设置为多窗口模式,也不会生效(除了PIP模式):

        // Do not allow tasks not support multi window to be in a multi-window mode, unless it is in
        // pinned windowing mode.
        if (!supportsMultiWindow()) {
            final int candidateWindowingMode =
                    windowingMode != WINDOWING_MODE_UNDEFINED ? windowingMode : parentWindowingMode;
            if (WindowConfiguration.inMultiWindowMode(candidateWindowingMode)
                    && candidateWindowingMode != WINDOWING_MODE_PINNED) {
                getResolvedOverrideConfiguration().windowConfiguration.setWindowingMode(
                        WINDOWING_MODE_FULLSCREEN);
            }
        }

这一条便是针对不支持多窗口的Task进行的策略约束,这样即使某种情况下错误地对不支持多窗口进行了多窗口WindowingMode的设置,这里也能确保该Task不会进入到多窗口模式。

4)、计算Task的Configuration的部分属性:

        computeConfigResourceOverrides(getResolvedOverrideConfiguration(), newParentConfig);

这个方法很复杂,我们挑其中一处来看下:

        if (inOutConfig.orientation == ORIENTATION_UNDEFINED) {
            inOutConfig.orientation = (inOutConfig.screenWidthDp <= inOutConfig.screenHeightDp)
                    ? ORIENTATION_PORTRAIT : ORIENTATION_LANDSCAPE;
        }

很简单,根据screenWidthDp和screenHeightDp关系,判断inOutConfig的方向是横屏还是竖屏。

为什么要重新计算一下orientation属性,直接继承父容器的orientation不行吗?大部分时候这样做是没问题的,但是如果考虑到多窗口的情况,如分屏,那么情况就可能稍微不同。

假设此时手机的分辨率为900 * 1600,那么对于当前手机屏幕,或者默认屏幕DisplayContent来说,它的Configuration的orientation应该是竖屏,ORIENTATION_PORTRAIT,全屏应用对应的Task,其bounds也是900 * 1600,orientation自然也是ORIENTATION_PORTRAIT。

但如果是上下等分的分屏Task,那么其bounds就会变为900 * 800,此时宽大于高,那么其orientation就会变成ORIENTATION_LANDSCAPE,也就和全局的orientation不一致了,而实际上也应当如此。这个计算过程也可以体现出resolveOverrideConfiguration的作用,即根据当前container的具体情况,修正Configuration的数据信息,从而体现出每一个container的Configuration的差异性。

5)、总结来说,resolveOverrideConfiguration方法的作用是,对某一个WindowContainer的子类,如Task,对该子类的实例进行一些策略约束,策略约束的目的则是确保其实例的Configuration能够按照一定的规则和策略反映出当前实例的属性。而这个策略约束的结果,则是保存在了mResolvedOverrideConfiguration中。

4.5 将当前容器的Configuration改变应用到其所有子容器

看到ConfigurationContainer的onConfigurationChanged方法最后还有一点内容:

    public void onConfigurationChanged(Configuration newParentConfig) {
        // ......
        for (int i = getChildCount() - 1; i >= 0; --i) {
            dispatchConfigurationToChild(getChildAt(i), mFullConfiguration);
        }
    }

首先getChildAt方法是被WindowContainer重写:

    @Override
    protected E getChildAt(int index) {
        return mChildren.get(index);
    }

遍历所有子容器。

dispatchConfigurationToChild方法为:

    /**
     * Dispatches the configuration to child when {@link #onConfigurationChanged(Configuration)} is
     * called. This allows the derived classes to override how to dispatch the configuration.
     */
    void dispatchConfigurationToChild(E child, Configuration config) {
        child.onConfigurationChanged(config);
    }

即对所有的子容器,都调用其onConfigurationChanged方法,传入的config,则是父container的mFullConfiguration。

如果子容器没有主动申请一个不为WINDOWING_MODE_UNDEFINED的WindowingMode,那么子容器的mFullConfiguration的WindowingMode将会被父容器的mFullConfiguration的WindowingMode覆盖,或者说子容器的WindowingMode将会继承父容器的WindowingMode。

最终我们本次为Task请求的WindowingMode,WINDOWING_MODE_FREEFORM,将会应用到该Task的所有子container上:

        #6 Task=46 type=standard mode=freeform override-mode=freeform requested-bounds=[0,0][1080,2418] bounds=[0,102][1080,2520]
         #0 ActivityRecord{cfe44 u0 com.google.android.apps.messaging/.ui.ConversationListActivity t46} type=standard mode=freeform override-mode=undefined requested-bounds=[0,0][0,0] boun
ds=[0,102][1080,2520]
          #0 13bc593 com.google.android.apps.messaging/com.google.android.apps.messaging.ui.ConversationListActivity type=standard mode=freeform override-mode=undefined requested-bounds=[0
,0][0,0] bounds=[0,102][1080,2520]

并且只有Task#46的mRequestedOverrideConfiguration的WindowingMode为freeform,其子container的mRequestedOverrideConfiguration的WindowingMode仍然为undefined。

最后,别忘了该方法也和resolveOverrideConfiguration方法一样,支持其子类进行重写:

4【Android 12】ConfigurationContainer类_第4张图片

从而使得这些子类能够在Configuration发生改变的时候,更加及时地做出响应。

4.6 小结

根据以上分析,我们可以得出onConfigurationChanged回调触发的两种情况:

  • 当前container请求了某些属性的改变,希望立即生效,这是通过调用onRequestedOverrideConfigurationChanged方法进而调用onConfigurationChanged方法的方式实现的。
  • 当前container的父container的Configuration发生了改变,并且希望能够立即广播给其所有的子container,这是通过调用父container的onConfigurationChanged方法进而调用子container的onConfigurationChanged方法来实现的。

另外,我们可以总结出Configuration更新的一般流程,即onConfigurationChanged方法的主要内容。

1)、首先mRequestedOverrideConfiguration中保存的是当前container自己请求的一部分特有信息,一般是通过ConfigurationContainer提供的各种setXXX方法进行请求地,这部分信息不会被父容器的Configuration所覆盖。

2)、onConfigurationChanged方法中,首先调用当前container的resolveOverrideConfiguration方法,该方法将mRequestedOverrideConfiguration赋值给mResolvedOverrideConfiguration,然后基于每个container类独有的策略规则,为mResolvedOverrideConfiguration施加约束和进行修正。

3)、将mFullConfiguration赋值为传入的newParentConfig的值。

4)、在newParentConfig的基础上,mFullConfiguration根据第二步得到的mResolvedOverrideConfiguration的值进行更新,得到最终的mFullConfiguration。

5)、得到最终的mFullConfiguration后,调用当前container的所有子container的onConfigurationChanged方法,使用该mFullConfiguration来更新所有子container的mFullConfiguration。

4【Android 12】ConfigurationContainer类_第5张图片

5 Configuration在层级结构中的更新

现在我们知道了,在父容器的onConfigurationChanged方法中,父容器在更新了自己的mFullConfiguration后,会将其mFullConfiguration作为传参,继续调用子容器的onConfigurationChanged方法,进而形成递归调用。

5.1 Configuration更新的顺序

第一个问题是,哪个container的onConfigurationChanged方法在某一次Configuration更新的时候被第一个调用?

之前有简单提到过,ConfigurationContainer作为WindowContainer的父类,并没有参与到层级结构的创建之中,和层级结构相关的方法也都是抽象的:

    abstract protected int getChildCount();

    abstract protected E getChildAt(int index);

    abstract protected ConfigurationContainer getParent();

这些抽象方法的具体实现则是在WindowContainer中,因此Configuration传递所依托的层级结构,就是WindowContainer建立起的层级结构,之前在分析WindowContainer类的时候已经分析过了,直接拿当时的一张图:

4【Android 12】ConfigurationContainer类_第6张图片

这个图并不反映系统中真实的container层级结构,但是可以作为参考,具体某一时刻的层级结构信息则可以通过执行dumpsys activity containers命令查看。

该图,以RootWindowContainer为根节点,各个叶子节点则是代表每一个窗口的WindowState对象,越靠左则在层级结构中的层级越高。

另外查看ConfigurationContainer的onConfigurationChanged方法调用子容器的顺序:

        for (int i = getChildCount() - 1; i >= 0; --i) {
            dispatchConfigurationToChild(getChildAt(i), mFullConfiguration);
        }

是从高到低,那么拿上面的图为例:

1)、第一个触发onConfigurationChanged方法的是根节点,RootWindowContainer。

2)、此后按照从左至右,从上至下的顺序,该层级结构中的每一个节点对应的container依次回调onConfigurationChanged方法,最终所有的container的onConfigurationChanged方法都能得到执行。

而事实上也的确如此,以一次转屏的动作为例,log的信息的确和上面分析一致,各个container的onConfigurationChanged方法调用的顺序也就是执行dumpsys activity containers命令后,打印的各个container的顺序,log比较多,这里就不贴了。

5.2 Configuration更新的初值

第二个问题是,在某一次Configuration的更新中,Configuration的初值是如何得到的?

之前分析onConfigurationChanged方法的时候,知道该方法的传参是父容器的mFullConfiguration:

public void onConfigurationChanged(Configuration newParentConfig)

但是对于没有父容器的容器,即层级结构中的根节点,RootWindowContainer,它的onConfigurationChanged方法传入的Configuration是如何得到的?

还是以转屏为例,看下具体的方法调用堆栈:

12-21 21:54:05.106  1521  1608 W ukynho_config:         at com.android.server.wm.ConfigurationContainer.onConfigurationChanged(ConfigurationContainer.java:153)
12-21 21:54:05.106  1521  1608 W ukynho_config:         at com.android.server.wm.WindowContainer.onConfigurationChanged(WindowContainer.java:361)
12-21 21:54:05.106  1521  1608 W ukynho_config:         at com.android.server.wm.ActivityTaskManagerService.updateGlobalConfigurationLocked(ActivityTaskManagerService.java:4462)
12-21 21:54:05.106  1521  1608 W ukynho_config:         at com.android.server.wm.DisplayContent.updateDisplayOverrideConfigurationLocked(DisplayContent.java:5674)
12-21 21:54:05.106  1521  1608 W ukynho_config:         at com.android.server.wm.DisplayContent.updateDisplayOverrideConfigurationLocked(DisplayContent.java:5651)
12-21 21:54:05.106  1521  1608 W ukynho_config:         at com.android.server.wm.DisplayContent.sendNewConfiguration(DisplayContent.java:1437)
12-21 21:54:05.106  1521  1608 W ukynho_config:         at com.android.server.wm.DisplayRotation.continueRotation(DisplayRotation.java:697)
12-21 21:54:05.106  1521  1608 W ukynho_config:         at com.android.server.wm.DisplayRotation.access$200(DisplayRotation.java:92)
12-21 21:54:05.106  1521  1608 W ukynho_config:         at com.android.server.wm.DisplayRotation$2.lambda$continueRotateDisplay$0(DisplayRotation.java:242)
12-21 21:54:05.106  1521  1608 W ukynho_config:         at com.android.server.wm.DisplayRotation$2$$ExternalSyntheticLambda0.accept(Unknown Source:10)
12-21 21:54:05.106  1521  1608 W ukynho_config:         at com.android.internal.util.function.pooled.PooledLambdaImpl.doInvoke(PooledLambdaImpl.java:295)
12-21 21:54:05.106  1521  1608 W ukynho_config:         at com.android.internal.util.function.pooled.PooledLambdaImpl.invoke(PooledLambdaImpl.java:204)
12-21 21:54:05.106  1521  1608 W ukynho_config:         at com.android.internal.util.function.pooled.OmniFunction.run(OmniFunction.java:97)
12-21 21:54:05.106  1521  1608 W ukynho_config:         at android.os.Handler.handleCallback(Handler.java:938)
12-21 21:54:05.106  1521  1608 W ukynho_config:         at android.os.Handler.dispatchMessage(Handler.java:99)
12-21 21:54:05.106  1521  1608 W ukynho_config:         at android.os.Looper.loopOnce(Looper.java:231)
12-21 21:54:05.106  1521  1608 W ukynho_config:         at android.os.Looper.loop(Looper.java:338)
12-21 21:54:05.106  1521  1608 W ukynho_config:         at android.os.HandlerThread.run(HandlerThread.java:67)
12-21 21:54:05.106  1521  1608 W ukynho_config:         at com.android.server.ServiceThread.run(ServiceThread.java:44)

关键的调用步骤为:

1)、DisplayContent.updateDisplayOverrideConfigurationLocked方法:

    boolean updateDisplayOverrideConfigurationLocked() {
        // ......

        Configuration values = new Configuration();
        computeScreenConfiguration(values);

        mAtmService.mH.sendMessage(PooledLambda.obtainMessage(
                ActivityManagerInternal::updateOomLevelsForDisplay, mAtmService.mAmInternal,
                mDisplayId));

        Settings.System.clearConfiguration(values);
        updateDisplayOverrideConfigurationLocked(values, null /* starting */,
                false /* deferResume */, mAtmService.mTmpUpdateConfigurationResult);
        return mAtmService.mTmpUpdateConfigurationResult.changes != 0;
    }

收到发送新的Configuration的请求后,首先调用computeScreenConfiguration方法,计算出一个新的Configuration。computeScreenConfiguration方法的具体内容则是,基于DisplayContent的DisplayInfo和其他的设备信息,计算出一个新的Configuration对象。

接着将计算好的Configuration发送给updateDisplayOverrideConfigurationLocked方法。

2)、DisplayContent.updateDisplayOverrideConfigurationLocked方法:

     * Updates override configuration specific for the selected display. If no config is provided,
     * new one will be computed in WM based on current display info.
     */
    boolean updateDisplayOverrideConfigurationLocked(Configuration values,
            ActivityRecord starting, boolean deferResume,
            ActivityTaskManagerService.UpdateConfigurationResult result) {

        int changes = 0;
        boolean kept = true;

        mAtmService.deferWindowLayout();
        try {
            if (values != null) {
                if (mDisplayId == DEFAULT_DISPLAY) {
                    // Override configuration of the default display duplicates global config, so
                    // we're calling global config update instead for default display. It will also
                    // apply the correct override config.
                    changes = mAtmService.updateGlobalConfigurationLocked(values,
                            false /* initLocale */, false /* persistent */,
                            UserHandle.USER_NULL /* userId */);
                } else {
                    changes = performDisplayOverrideConfigUpdate(values);
                }
            }

            if (!deferResume) {
                kept = mAtmService.ensureConfigAndVisibilityAfterUpdate(starting, changes);
            }
        } finally {
            mAtmService.continueWindowLayout();
        }

        if (result != null) {
            result.changes = changes;
            result.activityRelaunched = !kept;
        }
        return kept;
    }

这个方法很简单,继续将计算好的Configuration对象继续发送给ActivityTaskManagerService。

3)、ActivityTaskManagerService.updateGlobalConfigurationLocked方法:

    /** Update default (global) configuration and notify listeners about changes. */
    int updateGlobalConfigurationLocked(@NonNull Configuration values, boolean initLocale,
            boolean persistent, int userId) {

        mTempConfig.setTo(getGlobalConfiguration());
        final int changes = mTempConfig.updateFrom(values);
        
        // ......

        // Update stored global config and notify everyone about the change.
        mRootWindowContainer.onConfigurationChanged(mTempConfig);

        return changes;
    }

过滤不相关的信息,该方法首先通过getGlobalConfiguration方法取得全局Configuration:

    /**
     * Current global configuration information. Contains general settings for the entire system,
     * also corresponds to the merged configuration of the default display.
     */
    Configuration getGlobalConfiguration() {
        // Return default configuration before mRootWindowContainer initialized, which happens
        // while initializing process record for system, see {@link
        // ActivityManagerService#setSystemProcess}.
        return mRootWindowContainer != null ? mRootWindowContainer.getConfiguration()
                : new Configuration();
    }

也就是RootWindowContainer的mFullConfiguration,然后用传入的Configuration对其进行更新,最后调用RootWindowContainer的onConfigurationChanged方法。

那么本次Configuration更新的初值,则是基于上一次的全局Configuration,更新上通过DisplayContent.computeScreenConfiguration方法计算的Configuration得到的新的全局Configuration。

再进一步看到,RootWindowContainer是没有重写resolveOverrideConfiguration方法的,也没有主动去请求某些特殊属性,也就是说,RootWindowContainer的mRequestedOverrideConfiguration和mResolvedOverrideConfiguration全都是Configuration.EMPTY,那么按照mFullConfiguration的计算规则,当mResolvedOverrideConfiguration为Configuration.EMPTY的时候,mFullConfiguration的值就和onConfigurationChanged方法的传参newParentConfig的值相等,即RootWindowContainer的mFullConfiguration的值完全由传参newParentConfig决定。

而传参newParentConfig的值,则是在RootWindowContainer的mFullConfiguration的基础上,加上DisplayContent.computeScreenConfiguration计算出的Configuration得到的。

那么某一次Configuration更新,它的初值,即RootWindowContainer的onConfigurationChanged方法的传参newParentConfig,它的值,就是RootWindowContainer的上一次mFullConfiguration,加上DisplayContent.computeScreenConfiguration的结果得到的,也是本次RootWindowContainer的mFullConfiguration的最终值。

因为RootWindowContainer的mFullConfiguration也叫做global config,那么本次Configuration更新的初值,也就是global config,是通过:

globalConfig += DisplayContent.computeScreenConfiguration

得到的。

5.3 小结

通过本节可以得到的信息是:

1)、容器的Configuration更新顺序以容器通过WindowContainer组织起来的层级结构为基础,按照从高层级到低层级的顺序进行。

2)、RootWindowContainer的mFullConfiguration是作为全局Configuration的角色存在的。

3)、在一次Configuration更新时,首先根据当前的DisplayInfo和其他设备信息计算出一个新的Configuration,然后用这个新的Configuration去更新全局Configuration,得到能够反映当前系统实时情况的新的全局Configuration。

4)、各个容器的Configuration更新,便是以全局Configuration(或者是父容器的mFullConfiguration)为基础,再加上自己请求的部分(mRequestedOverrideConfiguration),以及策略约束部分(mResolvedOverrideConfiguration),从而得到一个新的mFullConfiguration,即完成了Configuration的更新。

6 mMergedOverrideConfiguration介绍

这个成员变量平时接触的很少,这里试着分析一下,可能有分析不对的地方,因此本节内容请慎重参考。

6.1 定义

首先是mMergedOverrideConfiguration的定义:

    /**
     * Contains merged override configuration settings from the top of the hierarchy down to this
     * particular instance. It is different from {@link #mFullConfiguration} because it starts from
     * topmost container's override config instead of global config.
     */
    private Configuration mMergedOverrideConfiguration = new Configuration();

包含了从WindowContainer层级结构顶层传到此container的合并的override configuration。它和mFullConfiguration不同的地方在于,它起始于层级结构中最顶层的那个container的override configuration,而不是全局configuration。

根据之前的分析,知道了全局configuration,就是层级结构中最顶层的那个container,RootWindowContainer的mFullConfiguration,那这里的”override config“如何理解?下面分析会讲到。

6.2 更新机制

继续看下mMergedOverrideConfiguration是如何更新的:

    public void onConfigurationChanged(Configuration newParentConfig) {
        mResolvedTmpConfig.setTo(mResolvedOverrideConfiguration);
        resolveOverrideConfiguration(newParentConfig);
        mFullConfiguration.setTo(newParentConfig);
        mFullConfiguration.updateFrom(mResolvedOverrideConfiguration);
        onMergedOverrideConfigurationChanged();
        // ......
    }

同样是在onConfigurationChanged方法中,mFullConfiguration更新完之后就调用了onMergedOverrideConfigurationChanged:

    /**
     * Update merged override configuration based on corresponding parent's config and notify all
     * its children. If there is no parent, merged override configuration will set equal to current
     * override config.
     * @see #mMergedOverrideConfiguration
     */
    void onMergedOverrideConfigurationChanged() {
        final ConfigurationContainer parent = getParent();
        if (parent != null) {
            mMergedOverrideConfiguration.setTo(parent.getMergedOverrideConfiguration());
            mMergedOverrideConfiguration.updateFrom(mResolvedOverrideConfiguration);
        } else {
            mMergedOverrideConfiguration.setTo(mResolvedOverrideConfiguration);
        }
        for (int i = getChildCount() - 1; i >= 0; --i) {
            final ConfigurationContainer child = getChildAt(i);
            child.onMergedOverrideConfigurationChanged();
        }
    }

这里看到,如果parent为null,那么直接将mResolvedOverrideConfiguration的值作为mMergedOverrideConfiguration的值,再根据注释的解释,当没有parent的时候,merged override configuration将等于current override config,这就可以解答6.1最后提出的问题,”override config“其实就是mResolvedOverrideConfiguration。

那么mMergedOverrideConfiguration定义处的注释意思就是,mFullConfiguration起始于global config,而mMergedOverrideConfiguration起始于层级结构根节点的mResolvedOverrideConfiguration。进一步说明就是,mFullConfiguration起始于RootWindowContainer的mFullConfiguration,而mMergedOverrideConfiguration起始于RootWindowContainer的mResolvedOverrideConfiguration。

对比一下mFullConfiguration和mMergedOverrideConfiguration的更新有何区别。

1)、mFullConfiguration,以父容器的mFullConfiguration为base,updateFrom上mResolvedOverrideConfiguration。当父容器为空,即当前容器为RootWindowContainer时,当前容器的mFullConfiguration以自己为base,updateFrom上DisplayContent.computeScreenConfiguration方法计算出的Configuration。

2)、mMergedOverrideConfiguration,以父容器的mMergedOverrideConfiguration为base,updateFrom上mResolvedOverrideConfiguration。当父容器为空,即当前容器为RootWindowContainer时,此时的mMergedOverrideConfiguration被直接设置为mResolvedOverrideConfiguration的值,根据onMergedOverrideConfigurationChanged方法注释中的这段话:

”If there is no parent, merged override configuration will set equal to current override config.“

说明mResolvedOverrideConfiguration,代表的就是注释中提到的”override config“,这也回答了6.1节最后的那个疑问。

mResolvedOverrideConfiguration,代表的是container自己的覆盖配置,这个覆盖配置是当前container的mFullConfiguration区别于父container的mFullConfiguration的部分,那对于某个container来说,它的mMergedOverrideConfiguration收集的信息就是从RootWindowContainer到当前container这条路径上的所有container的mResolvedOverrideConfiguration之和,或者说override config之和,即mMergedOverrideConfiguration的定义处的注释中的这句话:

”Contains merged override configuration settings from the top of the hierarchy down to this particular instance.“

这里使用比较粗暴的等式来演示一下。

假如当前层级结构只有4层:RootWindowContainer、DisplayContent、Task和ActivityRecord。

ActivityRecord的mMergedOverrideConfiguration计算方式为:

ActivityRecord.mMergedOverrideConfiguration
    = Task.mMergedOverrideConfiguration + ActivityRecord.mResolvedOverrideConfiguration

又因为

Task.mMergedOverrideConfiguration 
	= DisplayContent.mMergedOverrideConfiguration + Task.mResolvedOverrideConfiguration 

所以

ActivityRecord.mMergedOverrideConfiguration
    = Task.mMergedOverrideConfiguration + ActivityRecord.mResolvedOverrideConfiguration
    = DisplayContent.mMergedOverrideConfiguration + Task.mResolvedOverrideConfiguration 
    	+ ActivityRecord.mResolvedOverrideConfiguration

依次类推

ActivityRecord.mMergedOverrideConfiguration
    = Task.mMergedOverrideConfiguration + ActivityRecord.mResolvedOverrideConfiguration
    = DisplayContent.mMergedOverrideConfiguration + Task.mResolvedOverrideConfiguration 
    	+ ActivityRecord.mResolvedOverrideConfiguration
    = RootWindowContainer.mMergedOverrideConfiguration + DisplayContent.mResolvedOverrideConfiguration 
    	+ Task.mResolvedOverrideConfiguration + ActivityRecord.mResolvedOverrideConfiguration

RootWindowContainer的parent为null,所以

RootWindowContainer.mMergedOverrideConfiguration = RootWindowContainer.mResolvedOverrideConfiguration

那么

ActivityRecord.mMergedOverrideConfiguration
    = Task.mMergedOverrideConfiguration + ActivityRecord.mResolvedOverrideConfiguration
    = DisplayContent.mMergedOverrideConfiguration + Task.mResolvedOverrideConfiguration 
    	+ ActivityRecord.mResolvedOverrideConfiguration
    = RootWindowContainer.mMergedOverrideConfiguration + DisplayContent.mResolvedOverrideConfiguration 
    	+ Task.mResolvedOverrideConfiguration + ActivityRecord.mResolvedOverrideConfiguration
    = RootWindowContainer.mResolvedOverrideConfiguration + DisplayContent.mResolvedOverrideConfiguration 
    	+ Task.mResolvedOverrideConfiguration + ActivityRecord.mResolvedOverrideConfiguration

之前说过,RootWindowContainer是没有重写resolveOverrideConfiguration方法的,也没有主动去请求某些特殊属性,也就是说,RootWindowContainer的mRequestedOverrideConfiguration和mResolvedOverrideConfiguration全都是Configuration.EMPTY,而DisplayContent的mRequestedOverrideConfiguration和mResolvedOverrideConfiguration并不是Configuration.EMPTY,所以这里可以忽略RootWindowContainer.mResolvedOverrideConfiguration:

ActivityRecord.mMergedOverrideConfiguration
    = Task.mMergedOverrideConfiguration + ActivityRecord.mResolvedOverrideConfiguration
    = DisplayContent.mMergedOverrideConfiguration + Task.mResolvedOverrideConfiguration 
    	+ ActivityRecord.mResolvedOverrideConfiguration
    = RootWindowContainer.mMergedOverrideConfiguration + DisplayContent.mResolvedOverrideConfiguration 
    	+ Task.mResolvedOverrideConfiguration + ActivityRecord.mResolvedOverrideConfiguration
    = RootWindowContainer.mResolvedOverrideConfiguration + DisplayContent.mResolvedOverrideConfiguration 
    	+ Task.mResolvedOverrideConfiguration + ActivityRecord.mResolvedOverrideConfiguration
    = DisplayContent.mResolvedOverrideConfiguration 
    	+ Task.mResolvedOverrideConfiguration + ActivityRecord.mResolvedOverrideConfiguration    	

mResolvedOverrideConfiguration被视为”override config“,那mMergedOverrideConfiguration就是”merged override config“,即所有”override config“合并后的结果:

mergedOverrideConfig = overrideConfig1 + overrideConfig2 + ... + overrideConfigN

ActivityRecord的mFullConfiguration计算方式为:

ActivityRecord.mFullConfiguration 
	= Task.mFullConfiguration + ActivityRecord.mResolvedOverrideConfiguration
	= DisplayContent.mFullConfiguration + Task.mResolvedOverrideConfiguration + ActivityRecord.mResolvedOverrideConfiguration
	= RootWindowContainer.mFullConfiguration + DisplayContent.mResolvedOverrideConfiguration 
		+ Task.mResolvedOverrideConfiguration + ActivityRecord.mResolvedOverrideConfiguration
	= RootWindowContainer.mFullConfiguration + ActivityRecord.mMergedOverrideConfiguration

而RootWindowContainer.mFullConfiguration被视为”global config“,那么:

fullConfig = globalConfig + mergedOverrideCOnfig

某一个container的完整配置,等于”全局配置“,加上,”从层级结构根节点到当前节点的所有覆盖配置之和“。

6.3 使用

最后再看一下mMergedOverrideConfiguration使用的地方,mMergedOverrideConfiguration定义为private,那么别的类想要使用它只能通过ConfigurationContainer提供的getMergedOverrideConfiguration方法来获取:

    /**
     * Get merged override configuration from the top of the hierarchy down to this particular
     * instance. This should be reported to client as override config.
     */
    public Configuration getMergedOverrideConfiguration() {
        return mMergedOverrideConfiguration;
    }

注释说mMergedOverrideConfiguration应该作为override config被报导给客户端,这里的客户端指的应该是App进程,那如何理解呢?看一处获取mMergedOverrideConfiguration的地方,ActivityRecord.ensureActivityConfiguration:

    boolean ensureActivityConfiguration(int globalChanges, boolean preserveWindow,
            boolean ignoreVisibility) {
        // ......
        
        // Find changes between last reported merged configuration and the current one. This is used
        // to decide whether to relaunch an activity or just report a configuration change.
        final int changes = getConfigurationChanges(mTmpConfig);
        
        // Update last reported values.
        final Configuration newMergedOverrideConfig = getMergedOverrideConfiguration();

        setLastReportedConfiguration(getProcessGlobalConfiguration(), newMergedOverrideConfig);

        // ......

        if (changes == 0 && !forceNewConfig) {
            ProtoLog.v(WM_DEBUG_CONFIGURATION, "Configuration no differences in %s",
                    this);
            // There are no significant differences, so we won't relaunch but should still deliver
            // the new configuration to the client process.
            if (displayChanged) {
                scheduleActivityMovedToDisplay(newDisplayId, newMergedOverrideConfig);
            } else {
                scheduleConfigurationChanged(newMergedOverrideConfig);
            }
            return true;
        }

        // ......
    }

这里调用getMergedOverrideConfiguration获取到mMergedOverrideConfiguration,再将局部变量newMergedOverrideConfig指向它,newMergedOverrideConfig使用的地方有两处。

6.3.1 MergedConfiguration类

第一处是:

        setLastReportedConfiguration(getProcessGlobalConfiguration(), newMergedOverrideConfig);

这里getProcessGlobalConfiguration的定义为:

    /** Get process configuration, or global config if the process is not set. */
    private Configuration getProcessGlobalConfiguration() {
        return app != null ? app.getConfiguration() : mAtmService.getGlobalConfiguration();
    }

这里的app为WindowProcessController对象,WindowProcessController的Configuration更新和RootWindowContainer的Configuration更新一样,都是在ActivityTaskManagerService.updateGlobalConfigurationLocked中被触发了onConfigurationChanged方法,且传入的参数也是同一个。那么这里可以不管app是否为空,得到的Configuration都可以理解为global config。

setLastReportedConfiguration方法为:

    private void setLastReportedConfiguration(Configuration global, Configuration override) {
        mLastReportedConfiguration.setConfiguration(global, override);
    }

将global config和override config保存在mLastReportedConfiguration中,mLastReportedConfiguration的定义为:

    // Last configuration reported to the activity in the client process.
    private MergedConfiguration mLastReportedConfiguration;

上一次报导给App进程中的activity的Configuration。

MergedConfiguration定义为:

/**
 * Container that holds global and override config and their merge product.
 * Merged configuration updates automatically whenever global or override configs are updated via
 * setters.
 *
 * {@hide}
 */
public class MergedConfiguration implements Parcelable {

    private final Configuration mGlobalConfig = new Configuration();
    private final Configuration mOverrideConfig = new Configuration();
    private final Configuration mMergedConfig = new Configuration();
    
    // ......
}
  • mGlobalConfig,用来保存global config。

  • mOverrideConfig,用来保存override config。

  • mMergedConfig是两者的结合:

        /** Update merged config when global or override config changes. */
        private void updateMergedConfig() {
            mMergedConfig.setTo(mGlobalConfig);
            mMergedConfig.updateFrom(mOverrideConfig);
        }
    

而根据上面的代码,这里的mOverrideConfig实际上就是ActivityRecord的mMergedOverrideConfiguration,那么根据mMergedConfig的计算方式,感觉mMergedConfig的值应该和ActivityRecord的mFullConfiguration的值相等。

6.3.2 将mMergedOverrideConfiguration发送给App进程

newMergedOverrideConfig第二处使用的地方在:

        if (changes == 0 && !forceNewConfig) {
            ProtoLog.v(WM_DEBUG_CONFIGURATION, "Configuration no differences in %s",
                    this);
            // There are no significant differences, so we won't relaunch but should still deliver
            // the new configuration to the client process.
            if (displayChanged) {
                scheduleActivityMovedToDisplay(newDisplayId, newMergedOverrideConfig);
            } else {
                scheduleConfigurationChanged(newMergedOverrideConfig);
            }
            return true;
        }

该方法用来触发App进程中activity的Configuration的更新:

    private void scheduleConfigurationChanged(Configuration config) {
        if (!attachedToProcess()) {
            ProtoLog.w(WM_DEBUG_CONFIGURATION, "Can't report activity configuration "
                    + "update - client not running, activityRecord=%s", this);
            return;
        }
        try {
            ProtoLog.v(WM_DEBUG_CONFIGURATION, "Sending new config to %s, "
                    + "config: %s", this, config);

            mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(), appToken,
                    ActivityConfigurationChangeItem.obtain(config));
        } catch (RemoteException e) {
            // If process died, whatever.
        }
    }

具体细节暂不分析,也许后续会再开单独一篇来分析。

这一部分就是关于getMergedOverrideConfiguration方法注释中:

”This should be reported to client as override config.“

提到的地方,解释了6.3.1节中调用setLastReportedConfiguration方法的原因,以及mLastReportedConfiguration的作用,即在每次向App进程发送新的Configuration的时候,进行记录,等到下一次Configuration更新的时候,将之前记录的Configuration和这次的Configuration进行对比,判断是否需要重启activity,或者是触发activity的onConfigurationChanged回调等。

不过这里有一个疑问,为什么发送给App进程的是ActivityRecord的mMergedOverrideConfiguration,而不是更具代表性的mFullConfiguration?根据之前的分析,mMergedOverrideConfiguration代表的是所有覆盖配置之和,即”merged override config“,而mFullConfiguration代表的则是一个container的完整配置,它的值等于”global config“加上”merged override config“,也就是说,系统不希望把包含”global config“的信息发送给App进程,只想发送”override config“相关的信息,为什么这样做呢,暂时没有想到原因。

6.3.3 Configuation的对比

刚刚说了,mLastReportedConfiguration的作用是记录发送给App进程的Configuration,当下一次Configuration更新调用到来后,与这一次的Configuration进行对比,来判断是否触发activity的重启或者onConfigurationChanged回调。这个逻辑也是在ActivityRecord.ensureActivityConfiguration方法中,看一下Configuration是如何对比的:

        // Short circuit: if the two full configurations are equal (the common case), then there is
        // nothing to do.  We test the full configuration instead of the global and merged override
        // configurations because there are cases (like moving a task to the root pinned task) where
        // the combine configurations are equal, but would otherwise differ in the override config
        mTmpConfig.setTo(mLastReportedConfiguration.getMergedConfiguration());
        if (getConfiguration().equals(mTmpConfig) && !forceNewConfig && !displayChanged) {
            ProtoLog.v(WM_DEBUG_CONFIGURATION, "Configuration & display "
                    + "unchanged in %s", this);
            // It's possible that resolveOverrideConfiguration was called before mVisibleRequested
            // became true and mCompatDisplayInsets may not have been created so ensure
            // that mCompatDisplayInsets is created here.
            if (mVisibleRequested) {
                updateCompatDisplayInsets();
            }
            return true;
        }

注意这里进行比较的Configuration:

一个是通过getConfiguration方法得到的,ActivityRecord的mFullConfiguration。

另外一个是mLastReportedConfiguration的getMergedConfiguration返回的,即mLastReportedConfiguration的mMergedConfig。

而根据我们在6.3.1节中看到的mMergedConfig的计算方式:

    /** Update merged config when global or override config changes. */
    private void updateMergedConfig() {
        mMergedConfig.setTo(mGlobalConfig);
        mMergedConfig.updateFrom(mOverrideConfig);
    }

MergedConfiguration.mMergedConfig的值的计算方式和ActivityRecord.mFullConfiguration的计算方式本质是相同的,不同点在于mFullConfiguration更新的应该更加频繁一点,而mLastReportedConfiguration只有在将一个新的MergedConfiguration发送给App进程的时候才会更新。因此mFullConfiguration可以作为实时的Configuration,当准备向App进程发送新的Configuration时,拿实时的Configuration与上一次发送给App进程的Configuration进行对比,如果两者不一致再向App进程发送新的Configuration。

而实际上,ConfigurationContainer.getMergedOverrideConfiguration方法使用的场景也很有限,大部分都是在系统进程与App进程通信,需要更新App进程中activity的Configuration的时候。而且有的时候,发送给App进程的也不只是mMergedOverrideConfiguration,而是MergedConfiguration,如WindowState.fillClientWindowFramesAndConfiguration:

    /**
     * Fills the given window frames and merged configuration for the client.
     *
     * @param outFrames The frames that will be sent to the client.
     * @param outMergedConfiguration The configuration that will be sent to the client.
     * @param useLatestConfig Whether to use the latest configuration.
     * @param relayoutVisible Whether to consider visibility to use the latest configuration.
     */
    void fillClientWindowFramesAndConfiguration(ClientWindowFrames outFrames,
            MergedConfiguration outMergedConfiguration, boolean useLatestConfig,
            boolean relayoutVisible) {
        // ......

        // Note: in the cases where the window is tied to an activity, we should not send a
        // configuration update when the window has requested to be hidden. Doing so can lead to
        // the client erroneously accepting a configuration that would have otherwise caused an
        // activity restart. We instead hand back the last reported {@link MergedConfiguration}.
        if (useLatestConfig || (relayoutVisible && (shouldCheckTokenVisibleRequested()
                || mToken.isVisibleRequested()))) {
            final Configuration globalConfig = getProcessGlobalConfiguration();
            final Configuration overrideConfig = getMergedOverrideConfiguration();
            outMergedConfiguration.setConfiguration(globalConfig, overrideConfig);
            if (outMergedConfiguration != mLastReportedConfiguration) {
                mLastReportedConfiguration.setTo(outMergedConfiguration);
            }
        } else {
            outMergedConfiguration.setTo(mLastReportedConfiguration);
        }
        mLastConfigReportedToClient = true;
    }

这里返回给App进程的就是”global config“和”merged override config“,比起只返回”merged override config“,信息肯定要更全一点。

6.4 小结

这里根据注释中对各个变量的称呼依次介绍。

  • ”global config“,代表的是层级结构根节点的container的mFullConfiguration,即RootWindowContainer的mFullConfiguration,所有container的mFullConfiguration都基于此进行更新。

  • ”override config“,mResolvedOverrideConfiguration,代表的是当前container的覆盖配置,“覆盖”的含义则是当前container的mFullConfiguration覆盖父container的mFullConfiguration,覆盖的那一部分,或者说当前container的mFullConfiguration和父container的mFullConfiguration差异部分,就是mResolvedOverrideConfiguration,即“override config”。

  • ”merged override config“,mMergedOverrideConfiguration,代表的是从RootWindowContainer到当前container这条路径上的的所有container的”override config“(mResolvedOverrideConfiguration)之和。

  • “full config”,mFullConfiguration,当前container的完整配置。

那么mMergedOverrideConfiguration存在的意义便是:

fullConfig = globalConfig + mergedOverrideConfig

也就是,如果我们已经有了“global config”,那么还需要什么信息,就能获取到某一个activity的完整配置呢,这个缺少的信息也就是”merged override config“。

而这三者也是MergedConfiguration中成员变量的含义:

public class MergedConfiguration implements Parcelable {

    private final Configuration mGlobalConfig = new Configuration();
    private final Configuration mOverrideConfig = new Configuration();
    private final Configuration mMergedConfig = new Configuration();

因为MergedConfiguration.mMergedConfig的计算方式就是:

    /** Update merged config when global or override config changes. */
    private void updateMergedConfig() {
        mMergedConfig.setTo(mGlobalConfig);
        mMergedConfig.updateFrom(mOverrideConfig);
    }

另外我们看到,mMergedConfig的值只能通过updateMergedConfig方法计算得到,也就是MergedConfiguration没有直接提供对mMergedConfig直接赋值的方法,那么我们想要得到mMergedConfig的值,就必须传入mGlobalConfig和mOverrideConfig进行计算得出,而mOverrideConfig的值,取的就是mMergedOverrideConfiguration的值,除此之外系统中没有第二个值可以用来和mGlobalConfig一起计算得到mMergedConfig的值,这也就是mMergedOverrideConfiguration存在的意义。

最后看下“global config”,我们之前分析的情况是,“global config”的值就是RootWindowContainer的mFullConfiguration的值,这在大部分情况下都是成立的,但是当是WindowState的情况时,就不太一样:

    @Override
    public Configuration getConfiguration() {
        // If the process has not registered to any display area to listen to the configuration
        // change, we can simply return the mFullConfiguration as default.
        if (!registeredForDisplayAreaConfigChanges()) {
            return super.getConfiguration();
        }

        // We use the process config this window is associated with as the based global config since
        // the process can override its config, but isn't part of the window hierarchy.
        mTempConfiguration.setTo(getProcessGlobalConfiguration());
        mTempConfiguration.updateFrom(getMergedOverrideConfiguration());
        return mTempConfiguration;
    }

当WindowState附着到一个进程上后,WindowState对应的Configuration,不再是当前WindowState的mFullConfiguration。而是需要通过“global config”加上“merged override config”得到,而“global config”是通过getProcessGlobalConfiguration方法来取的:

    private Configuration getProcessGlobalConfiguration() {
        // For child windows we want to use the pid for the parent window in case the the child
        // window was added from another process.
        final WindowState parentWindow = getParentWindow();
        final int pid = parentWindow != null ? parentWindow.mSession.mPid : mSession.mPid;
        final Configuration processConfig =
                mWmService.mAtmService.getGlobalConfigurationForPid(pid);
        return processConfig;
    }

接着调用ActivityTaskManagerService.getGlobalConfigurationForPid方法:

    /**
     * Return the global configuration used by the process corresponding to the given pid.
     */
    Configuration getGlobalConfigurationForPid(int pid) {
        if (pid == MY_PID || pid < 0) {
            return getGlobalConfiguration();
        }
        synchronized (mGlobalLock) {
            final WindowProcessController app = mProcessMap.getProcess(pid);
            return app != null ? app.getConfiguration() : getGlobalConfiguration();
        }
    }

看到这里取的是当前窗口所在进程对应的WindowProcessController的mFullConfiguration,与进程强相关了,而不再是真正意义的全局Configuration,即那个全局独一份的Configuration。

而此时的“merged override config”,仍然是当前WindowState的mMergedOverrideConfiguration。

这个例子说明了,不同情况下,“global config”的值取的可能不一样,那么在这个时候,就只能通过

fullConfig = globalConfig + mergedOverrideConfig

的方式去计算得到“full config”了,那么“merged override config”存在的意义就显得更加重要了。

你可能感兴趣的:(WMS窗口层级结构,android)