之前在分析WMS的窗口层级结构时,看到WindowContainer的定义为:
class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<E>
implements Comparable<WindowContainer>, Animatable, SurfaceFreezer.Freezable {
看到WindowContainer其实还是有一个父类ConfigurationContainer的,但是由于ConfigurationContainer并不参与层级结构的构建,所以当时并没有对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的相关内容,方便我们后续分析。
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一起进行分发。
/**
* 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>
包含窗口配置或状态信息的类,用于那些直接或间接包含窗口的容器。
/**
* 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代表的都是屏幕的尺寸,但是少部分特殊模式下,它可能会小于屏幕的实际物理尺寸。
/**
* 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中针对尺寸兼容模式有了额外的逻辑,这部分看的比较少,暂时略过。
/** The current windowing mode of the configuration. */
private @WindowingMode int mWindowingMode;
/** The display windowing mode of the configuration */
private @WindowingMode int mDisplayWindowingMode;
目前的多窗口模式有以下几种:
/** 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。
/** 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类型的计算在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,就会抛出异常。
/** 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的介绍了。
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
这个成员变量放在最后一节分析。
有点抽象,还是从它们作用的地方入手,看下这些个成员的作用都是什么。
直接拿一个具体的例子看下。
假如我想将一个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。
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方法:
可以看出,ConfigurationContainer下的这类setXXX方法的逻辑都是类似的:
1)、先将请求的信息保存在了临时变量mRequestsTmpConfig中。
2)、接着调用onRequestedOverrideConfigurationChanged方法,传入mRequestsTmpConfig。
所以这类setXXX方法的关键在于onRequestedOverrideConfigurationChanged方法的调用。
在onRequestedOverrideConfigurationChanged方法中,重要的两个步骤则是:
1)、将请求的配置信息保存到mRequestedOverrideConfiguration中。
2)、调用onConfigurationChanged方法。
那么mRequestedOverrideConfiguration成员变量的作用,则是保存当前container主动为自己请求的一些特有的信息,目的是防止这部分的信息后续不会随着Configuration的更新而丢失。
而onRequestedOverrideConfigurationChanged方法虽然支持重写,但是最好在重写方法中调用:
super.onRequestedOverrideConfigurationChanged(overrideConfiguration);
确保本次请求的配置信息能够保存到mRequestedOverrideConfiguration中,以及ConfigurationContainer.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的。
上面的分析中并没有体现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的作用,目前重写该方法的子类有:
拿其中一个看一下,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中。
看到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方法一样,支持其子类进行重写:
从而使得这些子类能够在Configuration发生改变的时候,更加及时地做出响应。
根据以上分析,我们可以得出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。
现在我们知道了,在父容器的onConfigurationChanged方法中,父容器在更新了自己的mFullConfiguration后,会将其mFullConfiguration作为传参,继续调用子容器的onConfigurationChanged方法,进而形成递归调用。
第一个问题是,哪个container的onConfigurationChanged方法在某一次Configuration更新的时候被第一个调用?
之前有简单提到过,ConfigurationContainer作为WindowContainer的父类,并没有参与到层级结构的创建之中,和层级结构相关的方法也都是抽象的:
abstract protected int getChildCount();
abstract protected E getChildAt(int index);
abstract protected ConfigurationContainer getParent();
这些抽象方法的具体实现则是在WindowContainer中,因此Configuration传递所依托的层级结构,就是WindowContainer建立起的层级结构,之前在分析WindowContainer类的时候已经分析过了,直接拿当时的一张图:
这个图并不反映系统中真实的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比较多,这里就不贴了。
第二个问题是,在某一次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
得到的。
通过本节可以得到的信息是:
1)、容器的Configuration更新顺序以容器通过WindowContainer组织起来的层级结构为基础,按照从高层级到低层级的顺序进行。
2)、RootWindowContainer的mFullConfiguration是作为全局Configuration的角色存在的。
3)、在一次Configuration更新时,首先根据当前的DisplayInfo和其他设备信息计算出一个新的Configuration,然后用这个新的Configuration去更新全局Configuration,得到能够反映当前系统实时情况的新的全局Configuration。
4)、各个容器的Configuration更新,便是以全局Configuration(或者是父容器的mFullConfiguration)为基础,再加上自己请求的部分(mRequestedOverrideConfiguration),以及策略约束部分(mResolvedOverrideConfiguration),从而得到一个新的mFullConfiguration,即完成了Configuration的更新。
这个成员变量平时接触的很少,这里试着分析一下,可能有分析不对的地方,因此本节内容请慎重参考。
首先是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“如何理解?下面分析会讲到。
继续看下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的完整配置,等于”全局配置“,加上,”从层级结构根节点到当前节点的所有覆盖配置之和“。
最后再看一下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使用的地方有两处。
第一处是:
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的值相等。
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“相关的信息,为什么这样做呢,暂时没有想到原因。
刚刚说了,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“,信息肯定要更全一点。
这里根据注释中对各个变量的称呼依次介绍。
”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”存在的意义就显得更加重要了。