本文主要的重点放在Configuration的更新。
1 Resources的创建
根据一个Resources对象在创建的时候是否与一个Activity有关联,可以将Resources分为Activity类型Resources和非Activity类型Resources。
1.1 Activity类型Resources的创建
创建Activity类型Resources的地方在ResourcesManager#createResourcesForActivityLocked:
@NonNull
private Resources createResourcesForActivityLocked(@NonNull IBinder activityToken,
@NonNull Configuration initialOverrideConfig, @Nullable Integer overrideDisplayId,
@NonNull ClassLoader classLoader, @NonNull ResourcesImpl impl,
@NonNull CompatibilityInfo compatInfo) {
final ActivityResources activityResources = getOrCreateActivityResourcesStructLocked(
activityToken);
cleanupReferences(activityResources.activityResources,
activityResources.activityResourcesQueue,
(r) -> r.resources);
Resources resources = compatInfo.needsCompatResources() ? new CompatResources(classLoader)
: new Resources(classLoader);
resources.setImpl(impl);
resources.setCallbacks(mUpdateCallbacks);
ActivityResource activityResource = new ActivityResource();
activityResource.resources = new WeakReference<>(resources,
activityResources.activityResourcesQueue);
activityResource.overrideConfig.setTo(initialOverrideConfig);
activityResource.overrideDisplayId = overrideDisplayId;
activityResources.activityResources.add(activityResource);
if (DEBUG) {
Slog.d(TAG, "- creating new ref=" + resources);
Slog.d(TAG, "- setting ref=" + resources + " with impl=" + impl);
}
return resources;
}
首先这里传入了一个IBinder类型的activityToken,这个是ActivityClientRecord的token对象,代表了当前Activity。
1.1.1 ActivityResources类的概念
final ActivityResources activityResources = getOrCreateActivityResourcesStructLocked(
activityToken);
接着通过ResourcesManager#getOrCreateActivityResourcesStructLocked获取一个ActivityResources对象,碰到了第一个新的类,ActivityResources。
ActivityResources类定义在ResourcesManager中:
/**
* Class containing the base configuration override and set of resources associated with an
* {@link Activity} or a {@link WindowContext}.
*/
private static class ActivityResources {
/**
* Override config to apply to all resources associated with the token this instance is
* based on.
*
* @see #activityResources
* @see #getResources(IBinder, String, String[], String[], String[], String[], Integer,
* Configuration, CompatibilityInfo, ClassLoader, List)
*/
public final Configuration overrideConfig = new Configuration();
/**
* The display to apply to all resources associated with the token this instance is based
* on.
*/
public int overrideDisplayId;
/** List of {@link ActivityResource} associated with the token this instance is based on. */
public final ArrayList<ActivityResource> activityResources = new ArrayList<>();
......
}
包含了与一个Activity或者WindowContext相关联的基础Configuration复写和Resources集合。
这也就是说,每一个Activity或者WindowContext,都可能对应多个Resources。在创建一个Activity或者WindowContext的时候,系统都会为其创建一个ActivityResources对象,这个ActivityResources的作用之一就是为Activity或者WindowContext保存与之联系的所有Resources对象,通过ActivityResources的成员变量activityResources。在下面会看到,这些Resources对象在创建的时候,会被封装为一个ActivityResource对象,然后添加到ActivityResources.activityResources中。
另外,ActivityResources还有一个overrideConfig成员变量,它会被应用到ActivityResources.activityResources队列中的所有Resources的Configuration,后面会看到它的具体作用。
1.1.2 ActivityResource类的概念
ActivityResource类也定义在ResourcesManager中:
/**
* Contains a resource derived from an {@link Activity} or {@link WindowContext} and information
* about how this resource expects its configuration to differ from the token's.
*
* @see ActivityResources
*/
// TODO: Ideally this class should be called something token related, like TokenBasedResource.
private static class ActivityResource {
/**
* The override configuration applied on top of the token's override config for this
* resource.
*/
public final Configuration overrideConfig = new Configuration();
/**
* If non-null this resource expects its configuration to override the display from the
* token's configuration.
*
* @see #applyDisplayMetricsToConfiguration(DisplayMetrics, Configuration)
*/
@Nullable
public Integer overrideDisplayId;
@Nullable
public WeakReference<Resources> resources;
private ActivityResource() {}
}
ActivityResources就是对Resources的一层封装,这里有两个值得关注的成员变量:
1)、resources,即当前ActivityResource对象保存的那个Resources对象的弱饮用。
2)、overrideConfig,保存了一个Activity或者WindowContext对象创建的时候的初始Configuration,这里我们看到ResourcesManager#createResourcesForActivityLocked方法还有一个参数:
@NonNull Configuration initialOverrideConfig
也就是说在创建Activity或者WindowContext的时候,也可以指定一个初始Configuration,就保存在ActivityResource.overrideConfig中。这个初始Configuration保存的是Activity或者WindowContext在创建的同时伴随的那一份独特的信息。后面会看到,这个Configuration是很特殊的,在Configuration更新的时候不会被新传入的Configuration覆盖掉。
1.1.3 ResourcesManager#getOrCreateActivityResourcesStructLocked
有了一些基础知识后再去看ResourcesManager#getOrCreateActivityResourcesStructLocked方法:
private ActivityResources getOrCreateActivityResourcesStructLocked(
@NonNull IBinder activityToken) {
ActivityResources activityResources = mActivityResourceReferences.get(activityToken);
if (activityResources == null) {
activityResources = new ActivityResources();
mActivityResourceReferences.put(activityToken, activityResources);
}
return activityResources;
}
ResourcesManager#getOrCreateActivityResourcesStructLocked尝试从ResourcesManager的成员变量mActivityResourceReferences中根据activityToken去取相应的ActivityResources对象,如果找不到,就说明这是第一次为这个Activity创建相关联的Resources,那么新创建一个ActivityResources对象,并且将键值对<activityToken, activityResources>保存在ResourcesManager的成员变量mActivityResourceReferences中,mActivityResourceReferences定义是:
/**
* Each Activity or WindowToken may has a base override configuration that is applied to each
* Resources object, which in turn may have their own override configuration specified.
*/
@UnsupportedAppUsage
private final WeakHashMap<IBinder, ActivityResources> mActivityResourceReferences =
new WeakHashMap<>();
可以看到,ResourcesManager通过mActivityResourceReferences记录了每一个Activity对应的所有ActivityResources对象。
接下来其实就是上面所讲内容的具体实践:
Resources resources = compatInfo.needsCompatResources() ? new CompatResources(classLoader)
: new Resources(classLoader);
resources.setImpl(impl);
resources.setCallbacks(mUpdateCallbacks);
ActivityResource activityResource = new ActivityResource();
activityResource.resources = new WeakReference<>(resources,
activityResources.activityResourcesQueue);
activityResource.overrideConfig.setTo(initialOverrideConfig);
activityResource.overrideDisplayId = overrideDisplayId;
activityResources.activityResources.add(activityResource);
1)、创建了一个Resources对象。
2)、调用Resources#setImpl将传入的ResourcesImpl对象设置为这个新创建的Resources对象的mImpl成员变量。
3)、每一次为Activity创建Resources,都需要同时封装一层ActivityResource对象,并且将ActivityResource对象添加到activityResources.activityResources队列中。另外,这里还将ActivityResource对象的成员overrideConfig赋值为传入的initialOverrideConfig,这是ActivityResource.overrideConfig唯一赋值的地方。
1.2 非Activity类型Resources的创建
private @NonNull Resources createResourcesLocked(@NonNull ClassLoader classLoader,
@NonNull ResourcesImpl impl, @NonNull CompatibilityInfo compatInfo) {
cleanupReferences(mResourceReferences, mResourcesReferencesQueue);
Resources resources = compatInfo.needsCompatResources() ? new CompatResources(classLoader)
: new Resources(classLoader);
resources.setImpl(impl);
resources.setCallbacks(mUpdateCallbacks);
mResourceReferences.add(new WeakReference<>(resources, mResourcesReferencesQueue));
if (DEBUG) {
Slog.d(TAG, "- creating new ref=" + resources);
Slog.d(TAG, "- setting ref=" + resources + " with impl=" + impl);
}
return resources;
}
相较于Activity类型Resources创建的过程,非Activity类型Resources的创建就显得比较简单,这里看到ResourcesManager会把新创建的非Activity类型Resources添加到成员变量mResourceReferences:
/**
* A list of Resource references that can be reused.
*/
@UnsupportedAppUsage
private final ArrayList<WeakReference<Resources>> mResourceReferences = new ArrayList<>();
就像把mActivityResourceReferences记录的是Activity类型Resources一样,mResourceReferences记录的是所有非Activity类型Resources。
1.3 小结
1)、Activity类型Resources通过ResourcesManager#createResourcesForActivityLocked创建,非Activity类型Resources通过ResourcesManager#createResourcesLocked创建。
Activity类型Resources创建流程有两个,非Activity类型Resources创建流程只有一个。
调用堆栈总结为:
Activity类型Resources创建流程之一:
...
-> ContextImpl#createActivityContext
或
-> ContextImpl#createWindowContextResources
-> ResourcesManager#createBaseTokenResources
-> ResourcesManager#createResourcesForActivity
-> ResourcesManager#createResourcesForActivityLocked
Activity类型Resources和非Activity类型Resources共同的创建流程:
...
-> ResourcesManager#getResources
(根据此处传入的activityToken是否为null来决定是调用ResourcesManager#createResourcesForActivity还是ResourcesManager#createResources)
-> ResourcesManager#createResourcesForActivity(Activity类型Resources)
-> ResourcesManager#createResourcesForActivityLocked
-> ResourcesManager#createResources(非Activity类型Resources)
-> ResourcesManager#createResourcesLocked
2)、ResourcesManager为每一个Activity都创建了一个ActivityResources对象,用来保存所有和这个Activity有联系的Resources,将每一个Resource封装为了ActivityResource对象。
3)、ActivityResource的成员变量overrideConfig保存了每一个Activity类型Resource在创建时期的初始Configuration,它代表了每一个Activity=类型Resource的独特部分,在Activity类型Resources更新的时候有着重要作用。
4)、ResourcesManager成员变量mActivityResourceReferences保存的是所有Activity类型的Resources,成员变量mResourceReferences保存了所有非Activity类型Resources。
2 Resources的更新
2.1 所有Resources的更新
所有Resources的更新在ResourcesManager#applyConfigurationToResources。
调用堆栈第一处:
这一处涉及到了ConfigurationChangeItem,ConfigurationChangeItem使用的地方在WindowProcessController:
ActivityTaskManagerService#updateGlobalConfigurationLocked
-> WindowProcessController#onConfigurationChanged
-> WindowProcessController#updateConfiguration
-> WindowProcessController#dispatchConfiguration
ActivityRecord#onConfigurationChanged
-> WindowProcessController#dispatchConfiguration
第二处:
主要的还是第一处。
接下来分析具体代码。
/** Applies the global configuration to the managed resources. */
public final boolean applyConfigurationToResources(@NonNull Configuration config,
@Nullable CompatibilityInfo compat, @Nullable DisplayAdjustments adjustments) {
synchronized (mLock) {
try {
Trace.traceBegin(Trace.TRACE_TAG_RESOURCES,
"ResourcesManager#applyConfigurationToResources");
if (!mResConfiguration.isOtherSeqNewer(config) && compat == null) {
if (DEBUG || DEBUG_CONFIGURATION) {
Slog.v(TAG, "Skipping new config: curSeq="
+ mResConfiguration.seq + ", newSeq=" + config.seq);
}
return false;
}
int changes = mResConfiguration.updateFrom(config);
// ......
Configuration tmpConfig = new Configuration();
for (int i = mResourceImpls.size() - 1; i >= 0; i--) {
ResourcesKey key = mResourceImpls.keyAt(i);
WeakReference<ResourcesImpl> weakImplRef = mResourceImpls.valueAt(i);
ResourcesImpl r = weakImplRef != null ? weakImplRef.get() : null;
if (r != null) {
applyConfigurationToResourcesLocked(config, compat, tmpConfig, key, r);
} else {
mResourceImpls.removeAt(i);
}
}
return changes != 0;
} finally {
Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
}
}
}
将全局configuration的更新应用到受ResourcesManager管理的Resources对象上。
挑几处重要的地方分析。
2.1.1 更新ResourcesManager.mResConfiguration
if (!mResConfiguration.isOtherSeqNewer(config) && compat == null) {
if (DEBUG || DEBUG_CONFIGURATION) {
Slog.v(TAG, "Skipping new config: curSeq="
+ mResConfiguration.seq + ", newSeq=" + config.seq);
}
return false;
}
int changes = mResConfiguration.updateFrom(config);
这里ResourcesManager.mResConfiguration储存了每一次传入的Configuration的值,如果这一次传入的Configuration并不比ResourcesManager.mResConfiguration新,那么跳过这次更新。ResourcesManager.mResConfiguration的定义是:
/**
* The global configuration upon which all Resources are based. Multi-window Resources
* apply their overrides to this configuration.
*/
@UnsupportedAppUsage
private final Configuration mResConfiguration = new Configuration();
ResourcesManager.mResConfiguration作为全局Configuration来使用,所有Resources对象基于它来生成。这个具体怎么理解,可以看后续的分析。
另外这也是ResourcesManager.mResConfiguration更新的唯一地方。
2.1.2 ResourcesImpl类和ResourcesManager成员变量mResourceImpls
for (int i = mResourceImpls.size() - 1; i >= 0; i--) {
ResourcesKey key = mResourceImpls.keyAt(i);
WeakReference<ResourcesImpl> weakImplRef = mResourceImpls.valueAt(i);
ResourcesImpl r = weakImplRef != null ? weakImplRef.get() : null;
if (r != null) {
applyConfigurationToResourcesLocked(config, compat, tmpConfig, key, r);
} else {
mResourceImpls.removeAt(i);
}
}
接下来的内容就是遍历ResourcesManager.mResourceImpls中的每一个ResourcesImpl对象进行更新操作。
在分析applyConfigurationToResourcesLocked方法是如何更新ResourcesManager.mResourceImpls中的每一个ResourcesImpl对象之前,需要对ResourcesImpl类和ResourcesManager的成员变量mResourceImpls有一定的了解。
mResourceImpls,定义是:
/**
* A mapping of ResourceImpls and their configurations. These are heavy weight objects
* which should be reused as much as possible.
*/
@UnsupportedAppUsage
private final ArrayMap<ResourcesKey, WeakReference<ResourcesImpl>> mResourceImpls =
new ArrayMap<>();
注释说,这是ResourcesImpl对象到他们的Configuration的映射。
ResourcesKey对象从名字上来看,就是为了作为Map的主键存在的。ResourcesKey对象中有一个成员变量,mOverrdieConfiguration,也是保存了一个ResourcesKey独有的Configuration。
作为value的ResourcesImpl类,从名字上也可以看出,是Resources的实现类,真正持有Configuration和其他信息那个角色。
2.1.3 ResourcesManager.mResourceImpls内容的填充
跟踪一下ResourcesImpl是如何添加到ResourcesManager.mResourceImpls中的。
ResourcesManager.mResourceImpls添加数据是在ResourcesManager#findOrCreateResourcesImplForKeyLocked方法中:
/**
* Variant of {@link #findOrCreateResourcesImplForKeyLocked(ResourcesKey)} that attempts to
* load ApkAssets from a {@link ApkAssetsSupplier} when creating a new ResourcesImpl.
*/
private @Nullable ResourcesImpl findOrCreateResourcesImplForKeyLocked(
@NonNull ResourcesKey key, @Nullable ApkAssetsSupplier apkSupplier) {
ResourcesImpl impl = findResourcesImplForKeyLocked(key);
if (impl == null) {
impl = createResourcesImpl(key, apkSupplier);
if (impl != null) {
mResourceImpls.put(key, new WeakReference<>(impl));
}
}
return impl;
}
每次想获得一个ResourcesImpl对象的时候,先从ResourcesManager.mResourceImpls中去寻找与传入的ResourcesKey对象对应的那个ResourcesImpl对象,如果找不到,那么就会调用ResourcesImpl#createResourcesImpl去为当前ResourcesKey对象创建一个ResourcesImpl对象,然后把这个键值对添加到ResourcesManager.mResourceImpls中。这里也说明了,ResourcesKey和ResourcesImpl是一一对应的。
在ResourcesManager#findOrCreateResourcesImplForKeyLocked方法的调用情况的时候,看到以下两种调用地点:
1)、ResourcesManager#createResourcesForActivity
@Nullable
private Resources createResourcesForActivity(@NonNull IBinder activityToken,
@NonNull ResourcesKey key, @NonNull Configuration initialOverrideConfig,
@Nullable Integer overrideDisplayId, @NonNull ClassLoader classLoader,
@Nullable ApkAssetsSupplier apkSupplier) {
synchronized (mLock) {
if (DEBUG) {
Throwable here = new Throwable();
here.fillInStackTrace();
Slog.w(TAG, "!! Get resources for activity=" + activityToken + " key=" + key, here);
}
ResourcesImpl resourcesImpl = findOrCreateResourcesImplForKeyLocked(key, apkSupplier);
if (resourcesImpl == null) {
return null;
}
return createResourcesForActivityLocked(activityToken, initialOverrideConfig,
overrideDisplayId, classLoader, resourcesImpl, key.mCompatInfo);
}
}
这个正好对应的Activity类型Resources的创建流程,提前通过ResourcesManager#findOrCreateResourcesImplForKeyLocked方法获取一个ResourcesImpl对象,然后在ResourcesManager#createResourcesForActivityLocked方法中,再通过Resources#setImpl让新创建的Activity类型Resources保存这个ResourcesImpl对象。
2)、ResourcesManager#createResources
/**
* Creates a Resources object set with a ResourcesImpl object matching the given key.
*
* @param key The key describing the parameters of the ResourcesImpl object.
* @param classLoader The classloader to use for the Resources object.
* If null, {@link ClassLoader#getSystemClassLoader()} is used.
* @return A Resources object that gets updated when
* {@link #applyConfigurationToResources(Configuration, CompatibilityInfo)}
* is called.
*/
@Nullable
private Resources createResources(@NonNull ResourcesKey key, @NonNull ClassLoader classLoader,
@Nullable ApkAssetsSupplier apkSupplier) {
synchronized (mLock) {
if (DEBUG) {
Throwable here = new Throwable();
here.fillInStackTrace();
Slog.w(TAG, "!! Create resources for key=" + key, here);
}
ResourcesImpl resourcesImpl = findOrCreateResourcesImplForKeyLocked(key, apkSupplier);
if (resourcesImpl == null) {
return null;
}
return createResourcesLocked(classLoader, resourcesImpl, key.mCompatInfo);
}
}
这个正好对应的非Activity类型Resources的创建流程,提前通过ResourcesManager#findOrCreateResourcesImplForKeyLocked方法获取一个ResourcesImpl对象,然后在ResourcesManager#createResourcesLocked方法中,再通过Resources#setImpl让新创建的非Activity类型Resources保存这个ResourcesImpl对象。
从以上内容可以获取几点重要信息:
- ResourcesImpl和Resources是一对多的关系,多个Resources内部的mResourcesImpl成员变量可能是同一个。
- 不管是Activity类型Resources,还是非Activity类型Resources,它们内部的mResourcesImpl成员变量都被记录在ResourcesManager的成员变量mResourceImpls中。那么更新mResourceImpls表中记录的所有ResourcesImpl对象中保存的Configuration,其实就是更新所有Resources中保存的Configuration。
2.1.4 ResourcesImpl对象的创建
再看下创建ResourcesImpl对象的createResourcesImpl方法:
private @Nullable ResourcesImpl createResourcesImpl(@NonNull ResourcesKey key,
@Nullable ApkAssetsSupplier apkSupplier) {
final AssetManager assets = createAssetManager(key, apkSupplier);
if (assets == null) {
return null;
}
final DisplayAdjustments daj = new DisplayAdjustments(key.mOverrideConfiguration);
daj.setCompatibilityInfo(key.mCompatInfo);
final Configuration config = generateConfig(key);
final DisplayMetrics displayMetrics = getDisplayMetrics(generateDisplayId(key), daj);
final ResourcesImpl impl = new ResourcesImpl(assets, displayMetrics, config, daj);
if (DEBUG) {
Slog.d(TAG, "- creating impl=" + impl + " with key: " + key);
}
return impl;
}
这里有一点需要注意的是,这里创建ResourcesImpl用的Configuration是通过ResourcesManager#generateConfig生成的,上面提到的ResourcesManager的成员变量mResConfiguration的作用就是在这里,mResConfiguration可以通过ResourcesManager#getConfiguration方法得到:
public Configuration getConfiguration() {
synchronized (mLock) {
return mResConfiguration;
}
}
通过上面的分析可知,ResourcesManager每一次创建Resources对象时,都需要提前从mResourceImpls中获取一个ResourcesImpl对象,而mResourceImpls中的所有ResourcesImpl对象都是通过ResourcesManager#createResourcesImpl添加的。ResourcesManager#createResourcesImpl方法在创建ResourcesImpl对象的时候,需要向ResourcesImpl的构造方法中传入一个Configuration,这个Configuration对象是通过ResourcesManager#generateConfig方法生成的,而ResourcesManager#generateConfig返回的Configuration对象,则是基于ResourcesManager.mResConfiguration创建的:
private Configuration generateConfig(@NonNull ResourcesKey key) {
Configuration config;
final boolean hasOverrideConfig = key.hasOverrideConfiguration();
if (hasOverrideConfig) {
config = new Configuration(getConfiguration());
config.updateFrom(key.mOverrideConfiguration);
if (DEBUG) Slog.v(TAG, "Applied overrideConfig=" + key.mOverrideConfiguration);
} else {
config = getConfiguration();
}
return config;
}
以ResourcesManager.mResConfiguration为base,update上ResourcesKey.mOverrideConfiguration(如果Resourceskey不为null的话)。
这段逻辑展示了mResConfiguration的作用,就如mResConfiguration的注释所说的那样,所有的Resources的创建都是基于ResourcesManager.mResConfiguration的。
2.1.5 更新ResourcesImpl对象
可以继续往下分析了:
for (int i = mResourceImpls.size() - 1; i >= 0; i--) {
ResourcesKey key = mResourceImpls.keyAt(i);
WeakReference<ResourcesImpl> weakImplRef = mResourceImpls.valueAt(i);
ResourcesImpl r = weakImplRef != null ? weakImplRef.get() : null;
if (r != null) {
applyConfigurationToResourcesLocked(config, compat, tmpConfig, key, r);
} else {
mResourceImpls.removeAt(i);
}
}
这里遍历ResourcesManager.mResourceImpls,然后对于每一个ResourcesImpl对象,调用applyConfigurationToResourcesLocked去更新。
private void applyConfigurationToResourcesLocked(@NonNull Configuration config,
@Nullable CompatibilityInfo compat, Configuration tmpConfig,
ResourcesKey key, ResourcesImpl resourcesImpl) {
if (DEBUG || DEBUG_CONFIGURATION) {
Slog.v(TAG, "Changing resources "
+ resourcesImpl + " config to: " + config);
}
tmpConfig.setTo(config);
if (key.hasOverrideConfiguration()) {
tmpConfig.updateFrom(key.mOverrideConfiguration);
}
// Get new DisplayMetrics based on the DisplayAdjustments given to the ResourcesImpl. Update
// a copy if the CompatibilityInfo changed, because the ResourcesImpl object will handle the
// update internally.
DisplayAdjustments daj = resourcesImpl.getDisplayAdjustments();
if (compat != null) {
daj = new DisplayAdjustments(daj);
daj.setCompatibilityInfo(compat);
}
daj.setConfiguration(tmpConfig);
DisplayMetrics dm = getDisplayMetrics(generateDisplayId(key), daj);
resourcesImpl.updateConfiguration(tmpConfig, dm, compat);
}
重点看下Configuration是怎么更新的。
将本次传入的Configuration作为base,然后再根据ResourcesKey.mOverrideConfiguration进行update,得到最终的Configuration,然后再去调用ResourcesImpl#updateConfiguration去更新ResourcesImpl,最终在ResourcesImpl#calConfigChanges中,ResourcesImpl.mConfiguraton得到了更新。
这里有一个重要的点,如果当前ResourcesImp对象对应的ResourcesKey中保存的Configuration不是Configuration.EMPTY,那么最终得到的tmpConfig是经过ResourcesKey.mOverrideConfiguration的update的,不仅仅只是本次更新Configuration的传参config的值。
想象这样一种情况,比如这里传入的config.screenWidthDp为500dp,但是key.mOverrideConfiguration.screenWidthDp为600dp,那么最终tmpConfig中的screenWidthDp还是600dp,导致ResourcesImpl.mConfiguration.screenWidthDp还是600dp,没有更新为传入的500dp。也就是说,如果当前ResourcesImpl对应了一个ResourcesKey.mOverrideConfiguration不为Configuration.EMPTY的ResourcesKey,那么很可能经过这一次ResourcesManager.applyConfigurationToResourcesLocked,当前ResourcesImpl最终没有得到更新。
2.1.6 小结
经过ResourcesManager#applyConfigurationToResources,最终的结果是,所有ResourcesImpl对象中保存的Configuration对象得到更新,当然由于ResourcesKey中保存的Configuration不为Configuration.EMPTY,最终某些ResourcesImpl的Configuration也可能没有得到更新。
2.2 Activity类型Resources的更新
Activity Resources的更新在ResourcesManager#updateResourcesForActivity。
调用堆栈是:
看到是通过ActivityConfigurationChangeItem传递信息的,ActivityConfigurationChangeItem在服务端用的地方在ActivityRecord#scheduleConfigurationChanged:
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.
}
}
接下来看具体代码。
/**
* Updates an Activity's Resources object with overrideConfig. The Resources object
* that was previously returned by {@link #getResources(IBinder, String, String[], String[],
* String[], String[], Integer, Configuration, CompatibilityInfo, ClassLoader, List)} is still
* valid and will have the updated configuration.
*
* @param activityToken The Activity token.
* @param overrideConfig The configuration override to update.
* @param displayId Id of the display where activity currently resides.
*/
public void updateResourcesForActivity(@NonNull IBinder activityToken,
@Nullable Configuration overrideConfig, int displayId) {
try {
// ......
synchronized (mLock) {
final ActivityResources activityResources =
getOrCreateActivityResourcesStructLocked(activityToken);
boolean movedToDifferentDisplay = activityResources.overrideDisplayId != displayId;
if (Objects.equals(activityResources.overrideConfig, overrideConfig)
&& !movedToDifferentDisplay) {
// They are the same and no change of display id, no work to do.
return;
}
// Grab a copy of the old configuration so we can create the delta's of each
// Resources object associated with this Activity.
final Configuration oldConfig = new Configuration(activityResources.overrideConfig);
// Update the Activity's base override.
if (overrideConfig != null) {
activityResources.overrideConfig.setTo(overrideConfig);
} else {
activityResources.overrideConfig.unset();
}
// ......
// Rebase each Resources associated with this Activity.
final int refCount = activityResources.activityResources.size();
for (int i = 0; i < refCount; i++) {
final ActivityResource activityResource =
activityResources.activityResources.get(i);
final Resources resources = activityResource.resources.get();
if (resources == null) {
continue;
}
final ResourcesKey newKey = rebaseActivityOverrideConfig(activityResource,
overrideConfig, displayId);
if (newKey == null) {
continue;
}
// TODO(b/173090263): Improve the performance of AssetManager & ResourcesImpl
// constructions.
final ResourcesImpl resourcesImpl =
findOrCreateResourcesImplForKeyLocked(newKey);
if (resourcesImpl != null && resourcesImpl != resources.getImpl()) {
// Set the ResourcesImpl, updating it for all users of this Resources
// object.
resources.setImpl(resourcesImpl);
}
}
}
} finally {
Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
}
}
挑几点重要的分析。
2.2.1 遍历前的准备
final ActivityResources activityResources =
getOrCreateActivityResourcesStructLocked(activityToken);
boolean movedToDifferentDisplay = activityResources.overrideDisplayId != displayId;
if (Objects.equals(activityResources.overrideConfig, overrideConfig)
&& !movedToDifferentDisplay) {
// They are the same and no change of display id, no work to do.
return;
}
// Grab a copy of the old configuration so we can create the delta's of each
// Resources object associated with this Activity.
final Configuration oldConfig = new Configuration(activityResources.overrideConfig);
// Update the Activity's base override.
if (overrideConfig != null) {
activityResources.overrideConfig.setTo(overrideConfig);
} else {
activityResources.overrideConfig.unset();
}
1)、判断本次ResourcesManager#updateResourcesForActivity传入的新的Configuration和上一次传入的是否一致,如果一致那本次就不用更新了。这里是ActivityResources.overideConfig唯一更新的地方。
2)、获取到Activity的activityResources.overrideConfig的一份拷贝,因为现在我们要将activityResources.overrideConfig更新为传入的overrideConfig,但是后续还要用到更新前的activityResources.overrideConfig。
3)、将activityResources.overrideConfig更新为传入的overrideConfig。
2.2.2 遍历当前Activity对应的ActivityResources对象的activityResources成员
// Rebase each Resources associated with this Activity.
final int refCount = activityResources.activityResources.size();
for (int i = 0; i < refCount; i++) {
final ActivityResource activityResource =
activityResources.activityResources.get(i);
final Resources resources = activityResource.resources.get();
if (resources == null) {
continue;
}
final ResourcesKey newKey = rebaseActivityOverrideConfig(activityResource,
overrideConfig, displayId);
if (newKey == null) {
continue;
}
// TODO(b/173090263): Improve the performance of AssetManager & ResourcesImpl
// constructions.
final ResourcesImpl resourcesImpl =
findOrCreateResourcesImplForKeyLocked(newKey);
if (resourcesImpl != null && resourcesImpl != resources.getImpl()) {
// Set the ResourcesImpl, updating it for all users of this Resources
// object.
resources.setImpl(resourcesImpl);
}
}
这里可以看到和ResourcesManager#applyConfigurationToResources的差别,ResourcesManager#applyConfigurationToResources是遍历ResourcesManager.mResourceImpls,而这里只遍历和当前Activity相关联的Resources对象,也就是保存在ActivityResources.activityResources队列中的所有Resources。
这里先通过ResourcesManager#rebaseActivityOverrideConfig生成一个新ResourcesKey,这个ResourcesKey.mOverrideConfiguration就是基于传入的新Configuration和其他因素综合生成的最终的Configuration,然后再跟据这个ResourcesKey从ResourcesManager.mResourceImpls寻找一个对应的ResourcesImpl对象,找不到就创建一个,然后把新的ResourcesImpl对象通过Resources#setImpl赋值给Resources.mResourcesImpl成员变量,从而完成对Resources对象的更新。
重点看一下ResourcesManager#rebaseActivityOverrideConfig方法。
2.2.3 ResourcesManager#rebaseActivityOverrideConfig
/**
* Rebases an updated override config over any old override config and returns the new one
* that an Activity's Resources should be set to.
*/
@Nullable
private ResourcesKey rebaseActivityOverrideConfig(@NonNull ActivityResource activityResource,
@Nullable Configuration newOverrideConfig, int displayId) {
final Resources resources = activityResource.resources.get();
if (resources == null) {
return null;
}
// Extract the ResourcesKey that was last used to create the Resources for this
// activity.
final ResourcesKey oldKey = findKeyForResourceImplLocked(resources.getImpl());
if (oldKey == null) {
Slog.e(TAG, "can't find ResourcesKey for resources impl="
+ resources.getImpl());
return null;
}
// Build the new override configuration for this ResourcesKey.
final Configuration rebasedOverrideConfig = new Configuration();
if (newOverrideConfig != null) {
rebasedOverrideConfig.setTo(newOverrideConfig);
}
// ......
final boolean hasOverrideConfig =
!activityResource.overrideConfig.equals(Configuration.EMPTY);
if (hasOverrideConfig) {
rebasedOverrideConfig.updateFrom(activityResource.overrideConfig);
}
// ......
// Create the new ResourcesKey with the rebased override config.
final ResourcesKey newKey = new ResourcesKey(oldKey.mResDir,
oldKey.mSplitResDirs, oldKey.mOverlayPaths, oldKey.mLibDirs,
displayId, rebasedOverrideConfig, oldKey.mCompatInfo, oldKey.mLoaders);
if (DEBUG) {
Slog.d(TAG, "rebasing ref=" + resources + " from oldKey=" + oldKey
+ " to newKey=" + newKey + ", displayId=" + displayId);
}
return newKey;
}
1)、首先调用ResourcesManager#findKeyForResourceImplLocked在ResourcesManager.mResourcesImpl中查找当前ResourcesImpl有没有对应的ResourcesKey,没有直接返回。
2)、接下来为此次的要创建的新的ResourcesKey生成新的Configuration:rebasedOverrideConfig,它把传入的newOverrideConfig作为base,然后update上ActivityResource.overrideConfig得到终值。
3)、最后再根据最终的rebasedOverrideConfig去创建新的ResourcesKey。
这里和ResourcesManager#applyConfigurationForResources中更新Resources的思想是一样的,ActivityResource.overrideConfig唯一赋值的时间点是在Activity类型Resources创建的时候,那么ActivityResource.overrideConfig就保存了当前Activity类型Resouces创建的时候的初始Configuration,抽象点说就是这个Configuration是这个Resources与生俱来的,是这个Resources不同于其他Resources的重要特点,任何时候都不应该被丢弃,因此需要放在最后的时候再update上去,这样通过这个Resources获取的Configuration总是会包含这个Resources的特点部分。
2.2.4 小结
经过ResourcesManager#updateResourcesForActivity,最终的结果为,所有的Activity类型的Resources的mResourcesImpl成员变量被替换,这个过程中可能伴随着新的ResourcesImpl对象的创建,但是ResourcesImpl对象中保存的Configuration没有更新。
3 Log分析
这里我们通过Demo App的log来验证一下之前的纸上谈兵谈的正确不正确。
3.1 在Launcher界面点击Demo App图标启动App
12-17 11:26:35.375 7133 7133 I ukynho_res: ContextImpl#constructor ---- context = android.app.ContextImpl@8a62ae
12-17 11:26:35.384 7133 7133 I ukynho_res: ContextImpl#constructor ---- context = android.app.ContextImpl@a9dfbba
12-17 11:26:35.558 7133 7133 I ukynho_res: ContextImpl#constructor ---- context = android.app.ContextImpl@2c79e0
12-17 11:26:35.587 7133 7133 I ukynho_res: ContextImpl#constructor ---- context = android.app.ContextImpl@256d33f
12-17 11:26:35.646 7133 7133 I ukynho_res: ContextImpl#constructor ---- context = android.app.ContextImpl@763a4f8
12-17 11:26:35.693 7133 7133 I ukynho_res: ContextImpl#constructor ---- context = android.app.ContextImpl@df10a2f
12-17 11:26:35.957 7133 7133 I ukynho_res: ContextImpl#constructor ---- context = android.app.ContextImpl@d7e7b3
12-17 11:26:35.535 7133 7133 I ukynho_res: Resources#constructor ---- this = android.content.res.Resources@82d2a74
12-17 11:26:35.672 7133 7133 I ukynho_res: Resources#constructor ---- this = android.content.res.Resources@c3ad9c2
12-17 11:26:35.735 7133 7133 I ukynho_res: Resources#constructor ---- this = android.content.res.Resources@ca21027
12-17 11:26:35.840 7133 7133 I ukynho_res: Resources#constructor ---- this = android.content.res.Resources@19dc86c
12-17 11:26:35.967 7133 7133 I ukynho_res: Resources#constructor ---- this = android.content.res.Resources@8df4c0f
12-17 11:26:35.523 7133 7133 I ukynho_res: ResourcesImpl#constructor ---- resourcesImpl = android.content.res.ResourcesImpl@765ba61
12-17 11:26:35.658 7133 7133 I ukynho_res: ResourcesImpl#constructor ---- resourcesImpl = android.content.res.ResourcesImpl@ab5eb37
12-17 11:26:35.724 7133 7133 I ukynho_res: ResourcesImpl#constructor ---- resourcesImpl = android.content.res.ResourcesImpl@7863c28
首先我们看到总共创建了7个Context,但是只创建了5个Resources对象,ResourcesImpl更少,只有3个。
对应关系为:
12-17 11:26:35.377 7133 7133 I ukynho_res: ContextImpl#setResources ---- context = android.app.ContextImpl@8a62ae resources = android.content.res.Resources@dfa5116
12-17 11:26:35.540 7133 7133 I ukynho_res: ContextImpl#setResources ---- context = android.app.ContextImpl@a9dfbba resources = android.content.res.Resources@82d2a74
12-17 11:26:35.556 7133 7133 I ukynho_res: ContextImpl#setResources ---- context = android.app.ContextImpl@2c79e0 resources = android.content.res.Resources@82d2a74
12-17 11:26:35.589 7133 7133 I ukynho_res: ContextImpl#setResources ---- context = android.app.ContextImpl@256d33f resources = android.content.res.Resources@82d2a74
12-17 11:26:35.675 7133 7133 I ukynho_res: ContextImpl#setResources ---- context = android.app.ContextImpl@763a4f8 resources = android.content.res.Resources@c3ad9c2
12-17 11:26:35.742 7133 7133 I ukynho_res: ContextImpl#setResources ---- context = android.app.ContextImpl@df10a2f resources = android.content.res.Resources@ca21027
12-17 11:26:35.955 7133 7133 I ukynho_res: ContextImpl#setResources ---- context = android.app.ContextImpl@d7e7b3 resources = android.content.res.Resources@82d2a74
12-17 11:26:35.971 7133 7133 I ukynho_res: ContextImpl#setResources ---- context = android.app.ContextImpl@d7e7b3 resources = android.content.res.Resources@8df4c0f
12-17 11:26:35.537 7133 7133 I ukynho_res: Resources#setImpl ---- resources = android.content.res.Resources@82d2a74 mResourcesImpl = null impl = android.content.res.ResourcesImpl@765ba61
12-17 11:26:35.673 7133 7133 I ukynho_res: Resources#setImpl ---- resources = android.content.res.Resources@c3ad9c2 mResourcesImpl = null impl = android.content.res.ResourcesImpl@ab5eb37
12-17 11:26:35.737 7133 7133 I ukynho_res: Resources#setImpl ---- resources = android.content.res.Resources@ca21027 mResourcesImpl = null impl = android.content.res.ResourcesImpl@7863c28
12-17 11:26:35.842 7133 7133 I ukynho_res: Resources#setImpl ---- resources = android.content.res.Resources@19dc86c mResourcesImpl = null impl = android.content.res.ResourcesImpl@765ba61
12-17 11:26:35.969 7133 7133 I ukynho_res: Resources#setImpl ---- resources = android.content.res.Resources@8df4c0f mResourcesImpl = null impl = android.content.res.ResourcesImpl@765ba61
12-17 11:26:35.515 7133 7133 I ukynho_res: ResourcesManager#findOrCreateResourcesImplForKeyLocked ---- key = ResourcesKey{ mHash=593d1817 mOverrideConfig={0.0 ?mcc?mnc ?localeList ?layoutDir ?swdp ?wdp ?hdp ?density ?lsize ?long ?ldr ?wideColorGamut ?orien ?uimode ?night ?touch ?keyb/?/? ?nav/? winConfig={ mBounds=Rect(0, 0 - 0, 0) mAppBounds=null mMaxBounds=Rect(0, 0 - 0, 0) mWindowingMode=undefined mDisplayWindowingMode=undefined mActivityType=undefined mAlwaysOnTop=undefined mRotation=undefined} ?fontWeightAdjustment?ecid}}
12-17 11:26:35.515 7133 7133 I ukynho_res: ResourcesManager#findOrCreateResourcesImplForKeyLocked ---- existing resourcesIml = null
12-17 11:26:35.533 7133 7133 I ukynho_res: ResourcesManager#findOrCreateResourcesImplForKeyLocked ---- new resourcesIml = android.content.res.ResourcesImpl@765ba61
12-17 11:26:35.653 7133 7133 I ukynho_res: ResourcesManager#findOrCreateResourcesImplForKeyLocked ---- key = ResourcesKey{ mHash=2a5c3093 mOverrideConfig={0.0 ?mcc?mnc ?localeList ?layoutDir sw360dp w360dp h722dp 480dpi nrml long ?ldr ?wideColorGamut port ?uimode ?night -touch ?keyb/?/? ?nav/? winConfig={ mBounds=Rect(0, 0 - 0, 0) mAppBounds=null mMaxBounds=Rect(0, 0 - 0, 0) mWindowingMode=undefined mDisplayWindowingMode=undefined mActivityType=undefined mAlwaysOnTop=undefined mRotation=undefined} ?fontWeightAdjustment?ecid}}
12-17 11:26:35.653 7133 7133 I ukynho_res: ResourcesManager#findOrCreateResourcesImplForKeyLocked ---- existing resourcesIml = null
12-17 11:26:35.670 7133 7133 I ukynho_res: ResourcesManager#findOrCreateResourcesImplForKeyLocked ---- new resourcesIml = android.content.res.ResourcesImpl@ab5eb37
12-17 11:26:35.710 7133 7133 I ukynho_res: ResourcesManager#findOrCreateResourcesImplForKeyLocked ---- key = ResourcesKey{ mHash=7d7a14ea mOverrideConfig={0.93 ?mcc?mnc [en_US] ldltr sw360dp w360dp h722dp 480dpi nrml long port finger -keyb/v/h -nav/h winConfig={ mBounds=Rect(0, 0 - 1080, 2400) mAppBounds=Rect(0, 90 - 1080, 2256) mMaxBounds=Rect(0, 0 - 1080, 2400) mWindowingMode=fullscreen mDisplayWindowingMode=fullscreen mActivityType=standard mAlwaysOnTop=undefined mRotation=ROTATION_0} s.1 fontWeightAdjustment=0?ecid}}
12-17 11:26:35.710 7133 7133 I ukynho_res: ResourcesManager#findOrCreateResourcesImplForKeyLocked ---- existing resourcesIml = null
12-17 11:26:35.732 7133 7133 I ukynho_res: ResourcesManager#findOrCreateResourcesImplForKeyLocked ---- new resourcesIml = android.content.res.ResourcesImpl@7863c28
12-17 11:26:35.838 7133 7133 I ukynho_res: ResourcesManager#findOrCreateResourcesImplForKeyLocked ---- key = ResourcesKey{ mHash=593d1817 mOverrideConfig={0.0 ?mcc?mnc ?localeList ?layoutDir ?swdp ?wdp ?hdp ?density ?lsize ?long ?ldr ?wideColorGamut ?orien ?uimode ?night ?touch ?keyb/?/? ?nav/? winConfig={ mBounds=Rect(0, 0 - 0, 0) mAppBounds=null mMaxBounds=Rect(0, 0 - 0, 0) mWindowingMode=undefined mDisplayWindowingMode=undefined mActivityType=undefined mAlwaysOnTop=undefined mRotation=undefined} ?fontWeightAdjustment?ecid}}
12-17 11:26:35.839 7133 7133 I ukynho_res: ResourcesManager#findOrCreateResourcesImplForKeyLocked ---- existing resourcesIml = android.content.res.ResourcesImpl@765ba61
12-17 11:26:35.963 7133 7133 I ukynho_res: ResourcesManager#findOrCreateResourcesImplForKeyLocked ---- key = ResourcesKey{ mHash=593d1817 mOverrideConfig={0.0 ?mcc?mnc ?localeList ?layoutDir ?swdp ?wdp ?hdp ?density ?lsize ?long ?ldr ?wideColorGamut ?orien ?uimode ?night ?touch ?keyb/?/? ?nav/? winConfig={ mBounds=Rect(0, 0 - 0, 0) mAppBounds=null mMaxBounds=Rect(0, 0 - 0, 0) mWindowingMode=undefined mDisplayWindowingMode=undefined mActivityType=undefined mAlwaysOnTop=undefined mRotation=undefined} ?fontWeightAdjustment?ecid}}
12-17 11:26:35.964 7133 7133 I ukynho_res: ResourcesManager#findOrCreateResourcesImplForKeyLocked ---- existing resourcesIml = android.content.res.ResourcesImpl@765ba61
整理成表格:
| Context | Resources | ResourcesImpl | ResourcesKey |
|---|---|---|---|
| ContextImpl@8a62ae | Resources@dfa5116 | ||
| ContextImpl@a9dfbba | Resources@82d2a74 | ResourcesImpl@765ba61 | ResourcesKey{ mHash=593d1817 ......} |
| ContextImpl@2c79e0 | Resources@82d2a74 | ResourcesImpl@765ba61 | ResourcesKey{ mHash=593d1817 ......} |
| ContextImpl@256d33f | Resources@82d2a74 | ResourcesImpl@765ba61 | ResourcesKey{ mHash=593d1817 ......} |
| ContextImpl@763a4f8 | Resources@c3ad9c2 | ResourcesImpl@ab5eb37 | ResourcesKey{ mHash=2a5c3093......} |
| ContextImpl@df10a2f | Resources@ca21027 | ResourcesImpl@7863c28 | ResourcesKey{ mHash=7d7a14ea......} |
| ContextImpl@d7e7b3 | Resources@8df4c0f | ResourcesImpl@765ba61 | ResourcesKey{ mHash=593d1817 ......} |
| Resources@19dc86c | ResourcesImpl@765ba61 | ResourcesKey{ mHash=593d1817 ......} |
有一些Log可能没有打印出来,导致表格不是很全,但是无伤大雅。
3.1.1 Context和Resources的对应关系
Resources对于Context来说是一对多的关系,多个Context可能对应同一个Resources,在为Context创建Resources的时候,会先去寻找已经存在Resources中是否有合适的可以复用:
@Nullable
private Resources findResourcesForActivityLocked(@NonNull IBinder targetActivityToken,
@NonNull ResourcesKey targetKey, @NonNull ClassLoader targetClassLoader) {
ActivityResources activityResources = getOrCreateActivityResourcesStructLocked(
targetActivityToken);
final int size = activityResources.activityResources.size();
for (int index = 0; index < size; index++) {
ActivityResource activityResource = activityResources.activityResources.get(index);
Resources resources = activityResource.resources.get();
ResourcesKey key = resources == null ? null : findKeyForResourceImplLocked(
resources.getImpl());
if (key != null
&& Objects.equals(resources.getClassLoader(), targetClassLoader)
&& Objects.equals(key, targetKey)) {
return resources;
}
}
return null;
}
遍历当前Activity中所有的ResourcesImpl对象,看这些ResourcesImpl对应的ResourcesKey对象和传入的ResourcesKey对象targetKey是否一致。如果一致,说明此时对于当前传入的ResourcesKey对象,已经有一个ResourcesImp对象与其对应了,返回这个Resources对象即可。
3.1.2 Resources和ResourcesImpl的对应关系
ResourcesImpl对于Resources来说也是一对多的关系,多个Resources可能对应同一个ResourcesImpl,在为Resources创建ResourcesImpl的时候,会先去寻找已经存在ResourcesImpl中是否有合适的可以复用:
/**
* Variant of {@link #findOrCreateResourcesImplForKeyLocked(ResourcesKey)} that attempts to
* load ApkAssets from a {@link ApkAssetsSupplier} when creating a new ResourcesImpl.
*/
private @Nullable ResourcesImpl findOrCreateResourcesImplForKeyLocked(
@NonNull ResourcesKey key, @Nullable ApkAssetsSupplier apkSupplier) {
ResourcesImpl impl = findResourcesImplForKeyLocked(key);
if (impl == null) {
impl = createResourcesImpl(key, apkSupplier);
if (impl != null) {
mResourceImpls.put(key, new WeakReference<>(impl));
}
}
return impl;
}
如果已经有一个现有的ResourcesImpl对象与传入的ResourcesKey对应,那么就不需要再创建一个新的ResourcesImpl对象了。
3.1.3 比较的关键:ResourcesKey
这里看到,以上两种情况,决定是否复用的逻辑就是比较Resourceskey。
从之前的介绍中,我们知道创建Resources有两个入口,ResourcesManager#getResources和ResourcesManager#createBaseTokenResources,这两个方法有一个共同点:
public @Nullable Resources createBaseTokenResources(@NonNull IBinder token,
@Nullable String resDir,
@Nullable String[] splitResDirs,
@Nullable String[] legacyOverlayDirs,
@Nullable String[] overlayPaths,
@Nullable String[] libDirs,
int displayId,
@Nullable Configuration overrideConfig,
@NonNull CompatibilityInfo compatInfo,
@Nullable ClassLoader classLoader,
@Nullable List<ResourcesLoader> loaders) {
try {
Trace.traceBegin(Trace.TRACE_TAG_RESOURCES,
"ResourcesManager#createBaseActivityResources");
final ResourcesKey key = new ResourcesKey(
resDir,
splitResDirs,
combinedOverlayPaths(legacyOverlayDirs, overlayPaths),
libDirs,
displayId,
overrideConfig,
compatInfo,
loaders == null ? null : loaders.toArray(new ResourcesLoader[0]));
......
}
......
}
public Resources getResources(
@Nullable IBinder activityToken,
@Nullable String resDir,
@Nullable String[] splitResDirs,
@Nullable String[] legacyOverlayDirs,
@Nullable String[] overlayPaths,
@Nullable String[] libDirs,
@Nullable Integer overrideDisplayId,
@Nullable Configuration overrideConfig,
@NonNull CompatibilityInfo compatInfo,
@Nullable ClassLoader classLoader,
@Nullable List<ResourcesLoader> loaders) {
try {
Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, "ResourcesManager#getResources");
final ResourcesKey key = new ResourcesKey(
resDir,
splitResDirs,
combinedOverlayPaths(legacyOverlayDirs, overlayPaths),
libDirs,
overrideDisplayId != null ? overrideDisplayId : INVALID_DISPLAY,
overrideConfig,
compatInfo,
loaders == null ? null : loaders.toArray(new ResourcesLoader[0]));
......
}
......
}
都传入了一个Configuration类型的overrideConfig对象,并且基于这个overrideConfig对象生成了一个ResourcesKey对象,上面说了两种情况,比较的也就是这个ResourcesKey对象和ResourcesManager.mResourceImpls中保存的ResourcesKey对象。这说明了创建Resources的时候,会基于传入的overrideConfig生成一个ResourcesKey,这个ResourcesKey有独特性,在一定程度上体现了这个Resources的特点,至于具体ResourcesKey是怎么比较的,先挖个坑。
3.1.4 重点关注的两个Resources和ResourcesKey
在Demo App启动的过程中,总共创建了3个ResourcesKey,其中有一个的mOverrideConfiguration成员是Configuration.EMPTY,剩下两个分别是:
| Context | Resources | ResourcesImpl | ResourcesKey |
|---|---|---|---|
| ContextImpl@8a62ae | Resources@dfa5116 | ||
| ContextImpl@a9dfbba | Resources@82d2a74 | ResourcesImpl@765ba61 | ResourcesKey{ mHash=593d1817 ......} |
| ContextImpl@2c79e0 | Resources@82d2a74 | ResourcesImpl@765ba61 | ResourcesKey{ mHash=593d1817 ......} |
| ContextImpl@256d33f | Resources@82d2a74 | ResourcesImpl@765ba61 | ResourcesKey{ mHash=593d1817 ......} |
| ContextImpl@763a4f8 | Resources@c3ad9c2 | ResourcesImpl@ab5eb37 | ResourcesKey{ mHash=2a5c3093......} |
| ContextImpl@df10a2f | Resources@ca21027 | ResourcesImpl@7863c28 | ResourcesKey{ mHash=7d7a14ea......} |
| ContextImpl@d7e7b3 | Resources@8df4c0f | ResourcesImpl@765ba61 | ResourcesKey{ mHash=593d1817 ......} |
| Resources@19dc86c | ResourcesImpl@765ba61 | ResourcesKey{ mHash=593d1817 ......} |
ResourcesKey{ mHash=2a5c3093 mOverrideConfig={0.0 ?mcc?mnc ?localeList ?layoutDir sw360dp w360dp h722dp 480dpi nrml long ?ldr ?wideColorGamut port ?uimode ?night -touch ?keyb/?/? ?nav/? winConfig={ mBounds=Rect(0, 0 - 0, 0) mAppBounds=null mMaxBounds=Rect(0, 0 - 0, 0) mWindowingMode=undefined mDisplayWindowingMode=undefined mActivityType=undefined mAlwaysOnTop=undefined mRotation=undefined} ?fontWeightAdjustment?ecid}}
对应的ContextImpl@763a4f8通过ContextImpl#createSystemUiContext创建,对应的Resources@c3ad9c2通过ResourcesManager#createResourcesLocked得到,这就是我个人定义的非Activity类型Resources。
ResourcesKey{ mHash=7d7a14ea mOverrideConfig={0.93 ?mcc?mnc [en_US] ldltr sw360dp w360dp h722dp 480dpi nrml long port finger -keyb/v/h -nav/h winConfig={ mBounds=Rect(0, 0 - 1080, 2400) mAppBounds=Rect(0, 90 - 1080, 2256) mMaxBounds=Rect(0, 0 - 1080, 2400) mWindowingMode=fullscreen mDisplayWindowingMode=fullscreen mActivityType=standard mAlwaysOnTop=undefined mRotation=ROTATION_0} s.1 fontWeightAdjustment=0?ecid}}
对应的ContextImpl@df10a2f通过ContextImpl#createActivityContext创建,对应的Resources@ca21027通过ResourcesManager#createResourcesForActivityLocked得到,这就是我个人定义的Activity类型Resources。
3.2 从竖屏转到横屏
分别有一次ResourcesManager#applyConfigurationToResources和ResourcesManager#updateResourcesForActivity。
3.2.1 ResourcesManager#applyConfigurationToResources
这里遍历的是ResourcesManager.mResourceImpls,此时ResourcesManager.mResourceImpls有3个,我们这里只关注orientation这一个属性。
1)、先看ResourcesImpl@7863c28,它对应了Activity类型Resources。
key = ResourcesKey{ mHash=7d7a14ea mOverrideConfig={0.93 ?mcc?mnc [en_US] ldltr sw360dp w360dp h722dp 480dpi nrml long port finger -keyb/v/h -nav/h winConfig={ mBounds=Rect(0, 0 - 1080, 2400) mAppBounds=Rect(0, 90 - 1080, 2256) mMaxBounds=Rect(0, 0 - 1080, 2400) mWindowingMode=fullscreen mDisplayWindowingMode=fullscreen mActivityType=standard mAlwaysOnTop=undefined mRotation=ROTATION_0} s.1 fontWeightAdjustment=0?ecid}} resourcesImpl = android.content.res.ResourcesImpl@7863c28
对应的ResourcesKey.mOverrideConfiguration不为Configuration.EMPTY。
12-17 11:27:22.179 7133 7133 I ukynho_res: ResourcesManager#applyConfigurationToResourcesLocked ---- newConfig = {0.93 ?mcc?mnc [en_US] ldltr sw360dp w722dp h330dp 480dpi nrml long land finger -keyb/v/h -nav/h winConfig={ mBounds=Rect(0, 0 - 2400, 1080) mAppBounds=Rect(144, 0 - 2310, 1080) mMaxBounds=Rect(0, 0 - 2400, 1080) mWindowingMode=fullscreen mDisplayWindowingMode=fullscreen mActivityType=undefined mAlwaysOnTop=undefined mRotation=ROTATION_270} s.206 fontWeightAdjustment=0?ecid}
这个是传入的newConfig,方向为横屏。
12-17 11:27:22.180 7133 7133 I ukynho_res: ResourcesManager#applyConfigurationToResourcesLocked ---- after update, final config = {0.93 ?mcc?mnc [en_US] ldltr sw360dp w360dp h722dp 480dpi nrml long port finger -keyb/v/h -nav/h winConfig={ mBounds=Rect(0, 0 - 1080, 2400) mAppBounds=Rect(0, 90 - 1080, 2256) mMaxBounds=Rect(0, 0 - 1080, 2400) mWindowingMode=fullscreen mDisplayWindowingMode=fullscreen mActivityType=standard mAlwaysOnTop=undefined mRotation=ROTATION_0} s.1 fontWeightAdjustment=0?ecid}
但是由于ResourcesKey.mOverrideConfiguration不为Configuration.EMPTY,update上ResourcesKey.mOverrideConfiguration后方向又变为竖屏。
12-17 11:27:22.181 7133 7133 I ukynho_res: ResourcesImpl#updateConfiguration ---- resourcesImpl = android.content.res.ResourcesImpl@7863c28 newConfig = {0.93 ?mcc?mnc [en_US] ldltr sw360dp w360dp h722dp 480dpi nrml long port finger -keyb/v/h -nav/h winConfig={ mBounds=Rect(0, 0 - 1080, 2400) mAppBounds=Rect(0, 90 - 1080, 2256) mMaxBounds=Rect(0, 0 - 1080, 2400) mWindowingMode=fullscreen mDisplayWindowingMode=fullscreen mActivityType=standard mAlwaysOnTop=undefined mRotation=ROTATION_0} s.1 fontWeightAdjustment=0?ecid} oldConfig = {0.93 ?mcc?mnc [en_US] ldltr sw360dp w360dp h722dp 480dpi nrml long port finger -keyb/v/h -nav/h winConfig={ mBounds=Rect(0, 0 - 1080, 2400) mAppBounds=Rect(0, 90 - 1080, 2256) mMaxBounds=Rect(0, 0 - 1080, 2400) mWindowingMode=fullscreen mDisplayWindowingMode=fullscreen mActivityType=standard mAlwaysOnTop=undefined mRotation=ROTATION_0} s.1 fontWeightAdjustment=0?ecid}
最终传给ResourcesImpl#updateConfiguration的Configuration显示竖屏,说明由于ResourcesKey的存在,ResourcesImpl@7863c2在此次ResourcesManager#applyConfigurationToResources没有更新成功。
2)、i = 1, ResourcesImpl@765ba61。
12-17 11:27:22.183 7133 7133 I ukynho_res: ResourcesManager#applyConfigurationToResources ---- i = 1 key = ResourcesKey{ mHash=593d1817 mOverrideConfig={0.0 ?mcc?mnc ?localeList ?layoutDir ?swdp ?wdp ?hdp ?density ?lsize ?long ?ldr ?wideColorGamut ?orien ?uimode ?night ?touch ?keyb/?/? ?nav/? winConfig={ mBounds=Rect(0, 0 - 0, 0) mAppBounds=null mMaxBounds=Rect(0, 0 - 0, 0) mWindowingMode=undefined mDisplayWindowingMode=undefined mActivityType=undefined mAlwaysOnTop=undefined mRotation=undefined} ?fontWeightAdjustment?ecid}} resourcesImpl = android.content.res.ResourcesImpl@765ba61
对应的ResourcesKey.mOverrideConfiguration为Configuration.EMPTY。
12-17 11:27:22.184 7133 7133 I ukynho_res: ResourcesManager#applyConfigurationToResourcesLocked ---- newConfig = {0.93 ?mcc?mnc [en_US] ldltr sw360dp w722dp h330dp 480dpi nrml long land finger -keyb/v/h -nav/h winConfig={ mBounds=Rect(0, 0 - 2400, 1080) mAppBounds=Rect(144, 0 - 2310, 1080) mMaxBounds=Rect(0, 0 - 2400, 1080) mWindowingMode=fullscreen mDisplayWindowingMode=fullscreen mActivityType=undefined mAlwaysOnTop=undefined mRotation=ROTATION_270} s.206 fontWeightAdjustment=0?ecid}
这个是传入的newConfig,方向为横屏。
12-17 11:27:22.186 7133 7133 I ukynho_res: ResourcesImpl#updateConfiguration ---- resourcesImpl = android.content.res.ResourcesImpl@765ba61 newConfig = {0.93 ?mcc?mnc [en_US] ldltr sw360dp w722dp h330dp 480dpi nrml long land finger -keyb/v/h -nav/h winConfig={ mBounds=Rect(0, 0 - 2400, 1080) mAppBounds=Rect(144, 0 - 2310, 1080) mMaxBounds=Rect(0, 0 - 2400, 1080) mWindowingMode=fullscreen mDisplayWindowingMode=fullscreen mActivityType=undefined mAlwaysOnTop=undefined mRotation=ROTATION_270} s.206 fontWeightAdjustment=0?ecid} oldConfig = {0.93 ?mcc?mnc [en_US] ldltr sw360dp w360dp h722dp 480dpi nrml long port finger -keyb/v/h -nav/h winConfig={ mBounds=Rect(0, 0 - 1080, 2400) mAppBounds=Rect(0, 90 - 1080, 2256) mMaxBounds=Rect(0, 0 - 1080, 2400) mWindowingMode=fullscreen mDisplayWindowingMode=fullscreen mActivityType=undefined mAlwaysOnTop=undefined mRotation=ROTATION_0} s.80 fontWeightAdjustment=0?ecid}
由于它的ResourcesKey的mOverrideConfiguration是Configuration.EMPTY,那么直接更新成功,看到最终传给ResourcesImpl#updateConfiguration的Configuration为横屏,更新成功。
3)、i = 0, ResourcesImpl@ab5eb37。
12-17 11:27:22.188 7133 7133 I ukynho_res: ResourcesManager#applyConfigurationToResources ---- i = 0 key = ResourcesKey{ mHash=2a5c3093 mOverrideConfig={0.0 ?mcc?mnc ?localeList ?layoutDir sw360dp w360dp h722dp 480dpi nrml long ?ldr ?wideColorGamut port ?uimode ?night -touch ?keyb/?/? ?nav/? winConfig={ mBounds=Rect(0, 0 - 0, 0) mAppBounds=null mMaxBounds=Rect(0, 0 - 0, 0) mWindowingMode=undefined mDisplayWindowingMode=undefined mActivityType=undefined mAlwaysOnTop=undefined mRotation=undefined} ?fontWeightAdjustment?ecid}} resourcesImpl = android.content.res.ResourcesImpl@ab5eb37
对应的ResourcesKey.mOverrideConfiguration不为Configuration.EMPTY。
12-17 11:27:22.189 7133 7133 I ukynho_res: ResourcesManager#applyConfigurationToResourcesLocked ---- newConfig = {0.93 ?mcc?mnc [en_US] ldltr sw360dp w722dp h330dp 480dpi nrml long land finger -keyb/v/h -nav/h winConfig={ mBounds=Rect(0, 0 - 2400, 1080) mAppBounds=Rect(144, 0 - 2310, 1080) mMaxBounds=Rect(0, 0 - 2400, 1080) mWindowingMode=fullscreen mDisplayWindowingMode=fullscreen mActivityType=undefined mAlwaysOnTop=undefined mRotation=ROTATION_270} s.206 fontWeightAdjustment=0?ecid}
这个是传入的newConfig,方向为横屏。
12-17 11:27:22.189 7133 7133 I ukynho_res: ResourcesManager#applyConfigurationToResourcesLocked ---- after update, final config = {0.93 ?mcc?mnc [en_US] ldltr sw360dp w360dp h722dp 480dpi nrml long port -touch -keyb/v/h -nav/h winConfig={ mBounds=Rect(0, 0 - 2400, 1080) mAppBounds=Rect(144, 0 - 2310, 1080) mMaxBounds=Rect(0, 0 - 2400, 1080) mWindowingMode=fullscreen mDisplayWindowingMode=fullscreen mActivityType=undefined mAlwaysOnTop=undefined mRotation=ROTATION_270} s.206 fontWeightAdjustment=0?ecid}
但是由于ResourcesKey.mOverrideConfiguration不为Configuration.EMPTY,update上ResourcesKey.mOverrideConfiguration后方向又变为竖屏。
12-17 11:27:22.191 7133 7133 I ukynho_res: ResourcesImpl#updateConfiguration ----
resourcesImpl = android.content.res.ResourcesImpl@ab5eb37 newConfig = {0.93 ?mcc?mnc [en_US] ldltr sw360dp w360dp h722dp 480dpi nrml long port -touch -keyb/v/h -nav/h winConfig={ mBounds=Rect(0, 0 - 2400, 1080) mAppBounds=Rect(144, 0 - 2310, 1080) mMaxBounds=Rect(0, 0 - 2400, 1080) mWindowingMode=fullscreen mDisplayWindowingMode=fullscreen mActivityType=undefined mAlwaysOnTop=undefined mRotation=ROTATION_270} s.206 fontWeightAdjustment=0?ecid} oldConfig = {0.93 ?mcc?mnc [en_US] ldltr sw360dp w360dp h722dp 480dpi nrml long port -touch -keyb/v/h -nav/h winConfig={ mBounds=Rect(0, 0 - 1080, 2400) mAppBounds=Rect(0, 90 - 1080, 2256) mMaxBounds=Rect(0, 0 - 1080, 2400) mWindowingMode=fullscreen mDisplayWindowingMode=fullscreen mActivityType=undefined mAlwaysOnTop=undefined mRotation=ROTATION_0} s.80 fontWeightAdjustment=0?ecid}
最终传给ResourcesImpl#updateConfiguration的Configuration显示竖屏,说明由于ResourcesKey的存在,ResourcesImpl@ab5eb37在此次ResourcesManager#applyConfigurationToResources没有更新成功。
那么最终只有i = 1, ResourcesImpl@765ba61的情况更新成功。
3.2.2 ResourcesManager#updateResourcesForActivity
这里Activity类型Resources只有一个,即上一步中没有更新成功的ResourcesImpl@7863c28对应的Resources@ca21027。
12-17 11:27:22.212 7133 7133 I ukynho_res: ResourcesManager#updateResourcesForActivity ---- activity = android.os.BinderProxy@4f70d2d overrideConfig = {0.93 ?mcc?mnc [en_US] ldltr sw360dp w722dp h330dp 480dpi nrml long land finger -keyb/v/h -nav/h winConfig={ mBounds=Rect(0, 0 - 2400, 1080) mAppBounds=Rect(144, 0 - 2310, 1080) mMaxBounds=Rect(0, 0 - 2400, 1080) mWindowingMode=fullscreen mDisplayWindowingMode=fullscreen mActivityType=standard mAlwaysOnTop=undefined mRotation=ROTATION_270} s.2 fontWeightAdjustment=0?ecid} activityResources = android.app.ResourcesManager$ActivityResources@b9789c5 activityResources.overrideConfig = {0.93 ?mcc?mnc [en_US] ldltr sw360dp w360dp h722dp 480dpi nrml long port finger -keyb/v/h -nav/h winConfig={ mBounds=Rect(0, 0 - 1080, 2400) mAppBounds=Rect(0, 90 - 1080, 2256) mMaxBounds=Rect(0, 0 - 1080, 2400) mWindowingMode=fullscreen mDisplayWindowingMode=fullscreen mActivityType=standard mAlwaysOnTop=undefined mRotation=ROTATION_0} s.1 fontWeightAdjustment=0?ecid}
12-17 11:27:22.213 7133 7133 I ukynho_res: ResourcesManager#updateResourcesForActivity ---- i = 0 resources = android.content.res.Resources@ca21027
此时需要更新Resources对象的只有一个,Resources@ca21027。
12-17 11:27:22.214 7133 7133 I ukynho_res: ResourcesManager#rebaseActivityOverrideConfig ---- resources = android.content.res.Resources@ca21027 newOverrideConfig = {0.93 ?mcc?mnc [en_US] ldltr sw360dp w722dp h330dp 480dpi nrml long land finger -keyb/v/h -nav/h winConfig={ mBounds=Rect(0, 0 - 2400, 1080) mAppBounds=Rect(144, 0 - 2310, 1080) mMaxBounds=Rect(0, 0 - 2400, 1080) mWindowingMode=fullscreen mDisplayWindowingMode=fullscreen mActivityType=standard mAlwaysOnTop=undefined mRotation=ROTATION_270} s.2 fontWeightAdjustment=0?ecid} oldKey = ResourcesKey{ mHash=7d7a14ea mOverrideConfig={0.93 ?mcc?mnc [en_US] ldltr sw360dp w360dp h722dp 480dpi nrml long port finger -keyb/v/h -nav/h winConfig={ mBounds=Rect(0, 0 - 1080, 2400) mAppBounds=Rect(0, 90 - 1080, 2256) mMaxBounds=Rect(0, 0 - 1080, 2400) mWindowingMode=fullscreen mDisplayWindowingMode=fullscreen mActivityType=standard mAlwaysOnTop=undefined mRotation=ROTATION_0} s.1 fontWeightAdjustment=0?ecid}}
12-17 11:27:22.215 7133 7133 I ukynho_res: ResourcesManager#rebaseActivityOverrideConfig ---- activityResource.overrideConfig = {0.0 ?mcc?mnc ?localeList ?layoutDir ?swdp ?wdp ?hdp ?density ?lsize ?long ?ldr ?wideColorGamut ?orien ?uimode ?night ?touch ?keyb/?/? ?nav/? winConfig={ mBounds=Rect(0, 0 - 0, 0) mAppBounds=null mMaxBounds=Rect(0, 0 - 0, 0) mWindowingMode=undefined mDisplayWindowingMode=undefined mActivityType=undefined mAlwaysOnTop=undefined mRotation=undefined} ?fontWeightAdjustment?ecid} rebasedOverrideConfig = {0.93 ?mcc?mnc [en_US] ldltr sw360dp w722dp h330dp 480dpi nrml long land finger -keyb/v/h -nav/h winConfig={ mBounds=Rect(0, 0 - 2400, 1080) mAppBounds=Rect(144, 0 - 2310, 1080) mMaxBounds=Rect(0, 0 - 2400, 1080) mWindowingMode=fullscreen mDisplayWindowingMode=fullscreen mActivityType=standard mAlwaysOnTop=undefined mRotation=ROTATION_270} s.2 fontWeightAdjustment=0?ecid}
12-17 11:27:22.216 7133 7133 I ukynho_res: ResourcesManager#rebaseActivityOverrideConfig ---- newKey = ResourcesKey{ mHash=3f55d5d mOverrideConfig={0.93 ?mcc?mnc [en_US] ldltr sw360dp w722dp h330dp 480dpi nrml long land finger -keyb/v/h -nav/h winConfig={ mBounds=Rect(0, 0 - 2400, 1080) mAppBounds=Rect(144, 0 - 2310, 1080) mMaxBounds=Rect(0, 0 - 2400, 1080) mWindowingMode=fullscreen mDisplayWindowingMode=fullscreen mActivityType=standard mAlwaysOnTop=undefined mRotation=ROTATION_270} s.2 fontWeightAdjustment=0?ecid}} rebasedOverrideConfig = {0.93 ?mcc?mnc [en_US] ldltr sw360dp w722dp h330dp 480dpi nrml long land finger -keyb/v/h -nav/h winConfig={ mBounds=Rect(0, 0 - 2400, 1080) mAppBounds=Rect(144, 0 - 2310, 1080) mMaxBounds=Rect(0, 0 - 2400, 1080) mWindowingMode=fullscreen mDisplayWindowingMode=fullscreen mActivityType=standard mAlwaysOnTop=undefined mRotation=ROTATION_270} s.2 fontWeightAdjustment=0?ecid}
这里Resources@ca21027对应的ActivityResource.overrideConfig为Configuration.EMPTY,那么newKey的创建就完全使用了ResourcesManager#rebaseActivityOverrideConfig方法传入的newOverrideConfig。
12-17 11:27:22.217 7133 7133 I ukynho_res: ResourcesManager#findOrCreateResourcesImplForKeyLocked ---- key = ResourcesKey{ mHash=3f55d5d mOverrideConfig={0.93 ?mcc?mnc [en_US] ldltr sw360dp w722dp h330dp 480dpi nrml long land finger -keyb/v/h -nav/h winConfig={ mBounds=Rect(0, 0 - 2400, 1080) mAppBounds=Rect(144, 0 - 2310, 1080) mMaxBounds=Rect(0, 0 - 2400, 1080) mWindowingMode=fullscreen mDisplayWindowingMode=fullscreen mActivityType=standard mAlwaysOnTop=undefined mRotation=ROTATION_270} s.2 fontWeightAdjustment=0?ecid}}
12-17 11:27:22.217 7133 7133 I ukynho_res: ResourcesManager#findOrCreateResourcesImplForKeyLocked ---- existing resourcesIml = null
12-17 11:27:22.221 7133 7133 I ukynho_res: ResourcesImpl#constructor ---- resourcesImpl = android.content.res.ResourcesImpl@ff5ba1 config = {0.93 ?mcc?mnc [en_US] ldltr sw360dp w722dp h330dp 480dpi nrml long land finger -keyb/v/h -nav/h winConfig={ mBounds=Rect(0, 0 - 2400, 1080) mAppBounds=Rect(144, 0 - 2310, 1080) mMaxBounds=Rect(0, 0 - 2400, 1080) mWindowingMode=fullscreen mDisplayWindowingMode=fullscreen mActivityType=standard mAlwaysOnTop=undefined mRotation=ROTATION_270} s.2 fontWeightAdjustment=0?ecid}
12-17 11:27:22.223 7133 7133 I ukynho_res: ResourcesImpl#updateConfiguration ---- resourcesImpl = android.content.res.ResourcesImpl@ff5ba1 newConfig = {0.93 ?mcc?mnc [en_US] ldltr sw360dp w722dp h330dp 480dpi nrml long land finger -keyb/v/h -nav/h winConfig={ mBounds=Rect(0, 0 - 2400, 1080) mAppBounds=Rect(144, 0 - 2310, 1080) mMaxBounds=Rect(0, 0 - 2400, 1080) mWindowingMode=fullscreen mDisplayWindowingMode=fullscreen mActivityType=standard mAlwaysOnTop=undefined mRotation=ROTATION_270} s.2 fontWeightAdjustment=0?ecid} oldConfig = {1.0 ?mcc?mnc ?localeList ?layoutDir ?swdp ?wdp ?hdp ?density ?lsize ?long ?ldr ?wideColorGamut ?orien ?uimode ?night ?touch ?keyb/?/? ?nav/? winConfig={ mBounds=Rect(0, 0 - 0, 0) mAppBounds=null mMaxBounds=Rect(0, 0 - 0, 0) mWindowingMode=undefined mDisplayWindowingMode=undefined mActivityType=undefined mAlwaysOnTop=undefined mRotation=undefined} ?fontWeightAdjustment?ecid}
12-17 11:27:22.226 7133 7133 D ResourcesManager: - creating impl=android.content.res.ResourcesImpl@ff5ba1 with key: ResourcesKey{ mHash=3f55d5d mOverrideConfig={0.93 ?mcc?mnc [en_US] ldltr sw360dp w722dp h330dp 480dpi nrml long land finger -keyb/v/h -nav/h winConfig={ mBounds=Rect(0, 0 - 2400, 1080) mAppBounds=Rect(144, 0 - 2310, 1080) mMaxBounds=Rect(0, 0 - 2400, 1080) mWindowingMode=fullscreen mDisplayWindowingMode=fullscreen mActivityType=standard mAlwaysOnTop=undefined mRotation=ROTATION_270} s.2 fontWeightAdjustment=0?ecid}}
12-17 11:27:22.226 7133 7133 I ukynho_res: ResourcesManager#findOrCreateResourcesImplForKeyLocked ---- new resourcesIml = android.content.res.ResourcesImpl@ff5ba1
12-17 11:27:22.226 7133 7133 I ukynho_res: ResourcesManager#updateResourcesForActivity ---- i = 0 resourcesImpl = android.content.res.ResourcesImpl@ff5ba1 resources.getImpl() = android.content.res.ResourcesImpl@7863c28
12-17 11:27:22.227 7133 7133 I ukynho_res: Resources#setImpl ---- resources = android.content.res.Resources@ca21027 mResourcesImpl = android.content.res.ResourcesImpl@7863c28 impl = android.content.res.ResourcesImpl@ff5ba1
最终创建了一个新的ResourcesKey{ mHash=3f55d5d ......},并且创建了一个新的ResourcesImpl对象,ResouresImpl@ff5ba1,与之对应,最后通过Resources#setImpl方法将Resources@ca21027中的mResourcesImpl成员赋值为ResouresImpl@ff5ba1,完成了此次的Configuration更新。
虽然此时Resources@ca2102中的成员变量mResourcesImpl从ResourcesImpl@7863c28被替换为了ResouresImpl@ff5ba1,但是ResourcesImpl@7863c28仍然保存在ResourcesManager的mResourceImpls成员变量中,这为后续ResourcesImpl@7863c28的复用提供了支持。
| Context | Resources | ResourcesImpl | ResourcesKey |
|---|---|---|---|
| ContextImpl@8a62ae | Resources@dfa5116 | ||
| ContextImpl@a9dfbba | Resources@82d2a74 | ResourcesImpl@765ba61 | ResourcesKey{ mHash=593d1817 ......} |
| ContextImpl@2c79e0 | Resources@82d2a74 | ResourcesImpl@765ba61 | ResourcesKey{ mHash=593d1817 ......} |
| ContextImpl@256d33f | Resources@82d2a74 | ResourcesImpl@765ba61 | ResourcesKey{ mHash=593d1817 ......} |
| ContextImpl@763a4f8 | Resources@c3ad9c2 | ResourcesImpl@ab5eb37 | ResourcesKey{ mHash=2a5c3093......} |
| ContextImpl@df10a2f | Resources@ca21027 | ResouresImpl@ff5ba1 | ResourcesKey{ mHash=3f55d5d ......} |
| ContextImpl@d7e7b3 | Resources@8df4c0f | ResourcesImpl@765ba61 | ResourcesKey{ mHash=593d1817 ......} |
| Resources@19dc86c | ResourcesImpl@765ba61 | ResourcesKey{ mHash=593d1817 ......} | |
| ResourcesImpl@7863c28 | ResourcesKey{ mHash=7d7a14ea......} |
3.3 从横屏转到竖屏
分别有一次ResourcesManager#applyConfigurationToResources和ResourcesManager#updateResourcesForActivity。
3.3.1 ResourcesManager#applyConfigurationToResources
这里遍历的是ResourcesManager.mResourceImpls,此时ResourcesManager.mResourceImpls有4个,因为上一步新增了一个ResourcesImpl#ff5ba1,我们只看这个ResourcesImpl对象如何更新,同样我们这里只关注方向这一个属性。
1)、i = 3,ResourcesImpl@7863c28。
2)、i = 2, ResourcesImpl@765ba61。
3)、i = 1, ResourcesImpl@ab5eb37。
4)、i = 0, ResourcesImpl@ff5ba1。
12-17 11:27:34.325 7133 7133 I ukynho_res: ResourcesManager#applyConfigurationToResources ---- i = 0 key = ResourcesKey{ mHash=3f55d5d mOverrideConfig={0.93 ?mcc?mnc [en_US] ldltr sw360dp w722dp h330dp 480dpi nrml long* *land* *finger -keyb/v/h -nav/h winConfig={ mBounds=Rect(0, 0 - 2400, 1080) mAppBounds=Rect(144, 0 - 2310, 1080) mMaxBounds=Rect(0, 0 - 2400, 1080) mWindowingMode=fullscreen mDisplayWindowingMode=fullscreen mActivityType=standard mAlwaysOnTop=undefined mRotation=ROTATION_270} s.2 fontWeightAdjustment=0?ecid}} resourcesImpl = android.content.res.ResourcesImpl@ff5ba1
对应的ResourcesKey.mOverrideConfiguration不为Configuration.EMPTY。
12-17 11:27:34.326 7133 7133 I ukynho_res: ResourcesManager#applyConfigurationToResourcesLocked ---- newConfig = {0.93 ?mcc?mnc [en_US] ldltr sw360dp w360dp h722dp 480dpi nrml long port finger -keyb/v/h -nav/h winConfig={ mBounds=Rect(0, 0 - 1080, 2400) mAppBounds=Rect(0, 90 - 1080, 2256) mMaxBounds=Rect(0, 0 - 1080, 2400) mWindowingMode=fullscreen mDisplayWindowingMode=fullscreen mActivityType=undefined mAlwaysOnTop=undefined mRotation=ROTATION_0} s.265 fontWeightAdjustment=0?ecid}
这个是传入的newConfig,方向为竖屏。
12-17 11:27:34.326 7133 7133 I ukynho_res: ResourcesManager#applyConfigurationToResourcesLocked ---- after update, final config = {0.93 ?mcc?mnc [en_US] ldltr sw360dp w722dp h330dp 480dpi nrml long land finger -keyb/v/h -nav/h winConfig={ mBounds=Rect(0, 0 - 2400, 1080) mAppBounds=Rect(144, 0 - 2310, 1080) mMaxBounds=Rect(0, 0 - 2400, 1080) mWindowingMode=fullscreen mDisplayWindowingMode=fullscreen mActivityType=standard mAlwaysOnTop=undefined mRotation=ROTATION_270} s.2 fontWeightAdjustment=0?ecid}
但是由于ResourcesKey.mOverrideConfiguration不为Configuration.EMPTY,update上ResourcesKey.mOverrideConfiguration后方向又变为横屏。
12-17 11:27:34.328 7133 7133 I ukynho_res: ResourcesImpl#updateConfiguration ---- resourcesImpl = android.content.res.ResourcesImpl@ff5ba1 newConfig = {0.93 ?mcc?mnc [en_US] ldltr sw360dp w722dp h330dp 480dpi nrml long land finger -keyb/v/h -nav/h winConfig={ mBounds=Rect(0, 0 - 2400, 1080) mAppBounds=Rect(144, 0 - 2310, 1080) mMaxBounds=Rect(0, 0 - 2400, 1080) mWindowingMode=fullscreen mDisplayWindowingMode=fullscreen mActivityType=standard mAlwaysOnTop=undefined mRotation=ROTATION_270} s.2 fontWeightAdjustment=0?ecid} oldConfig = {0.93 ?mcc?mnc [en_US] ldltr sw360dp w722dp h330dp 480dpi nrml long land finger -keyb/v/h -nav/h winConfig={ mBounds=Rect(0, 0 - 2400, 1080) mAppBounds=Rect(144, 0 - 2310, 1080) mMaxBounds=Rect(0, 0 - 2400, 1080) mWindowingMode=fullscreen
最终传给ResourcesImpl#updateConfiguration的Configuration显示横屏,说明由于ResourcesKey的存在,ResourcesImpl@7863c2在此次ResourcesManager#applyConfigurationToResources没有更新成功。
3.3.2 ResourcesManager#updateResourcesForActivity
这里Activity Resources只有一个,即上一步中没有更新成功的ResourcesImpl@ff5ba1对应的Resources@ca21027。
12-17 11:27:34.367 7133 7133 I ukynho_res: ResourcesManager#updateResourcesForActivity ---- activity = android.os.BinderProxy@4f70d2d overrideConfig = {0.93 ?mcc?mnc [en_US] ldltr sw360dp w360dp h722dp 480dpi nrml long port finger -keyb/v/h -nav/h winConfig={ mBounds=Rect(0, 0 - 1080, 2400) mAppBounds=Rect(0, 90 - 1080, 2256) mMaxBounds=Rect(0, 0 - 1080, 2400) mWindowingMode=fullscreen mDisplayWindowingMode=fullscreen mActivityType=standard mAlwaysOnTop=undefined mRotation=ROTATION_0} s.3 fontWeightAdjustment=0?ecid} activityResources = android.app.ResourcesManager$ActivityResources@b9789c5 activityResources.overrideConfig = {0.93 ?mcc?mnc [en_US] ldltr sw360dp w722dp h330dp 480dpi nrml long land finger -keyb/v/h -nav/h winConfig={ mBounds=Rect(0, 0 - 2400, 1080) mAppBounds=Rect(144, 0 - 2310, 1080) mMaxBounds=Rect(0, 0 - 2400, 1080) mWindowingMode=fullscreen mDisplayWindowingMode=fullscreen mActivityType=standard mAlwaysOnTop=undefined mRotation=ROTATION_270} s.2 fontWeightAdjustment=0?ecid}
12-17 11:27:34.368 7133 7133 I ukynho_res: ResourcesManager#updateResourcesForActivity ---- i = 0 resources = android.content.res.Resources@ca21027
此时需要更新的只有Resources@ca21027。
12-17 11:27:34.369 7133 7133 I ukynho_res: ResourcesManager#rebaseActivityOverrideConfig ---- resources = android.content.res.Resources@ca21027 newOverrideConfig = {0.93 ?mcc?mnc [en_US] ldltr sw360dp w360dp h722dp 480dpi nrml long port finger -keyb/v/h -nav/h winConfig={ mBounds=Rect(0, 0 - 1080, 2400) mAppBounds=Rect(0, 90 - 1080, 2256) mMaxBounds=Rect(0, 0 - 1080, 2400) mWindowingMode=fullscreen mDisplayWindowingMode=fullscreen mActivityType=standard mAlwaysOnTop=undefined mRotation=ROTATION_0} s.3 fontWeightAdjustment=0?ecid} oldKey = ResourcesKey{ mHash=3f55d5d mOverrideConfig={0.93 ?mcc?mnc [en_US] ldltr sw360dp w722dp h330dp 480dpi nrml long land finger -keyb/v/h -nav/h winConfig={ mBounds=Rect(0, 0 - 2400, 1080) mAppBounds=Rect(144, 0 - 2310, 1080) mMaxBounds=Rect(0, 0 - 2400, 1080) mWindowingMode=fullscreen mDisplayWindowingMode=fullscreen mActivityType=standard mAlwaysOnTop=undefined mRotation=ROTATION_270} s.2 fontWeightAdjustment=0?ecid}}
12-17 11:27:34.369 7133 7133 I ukynho_res: ResourcesManager#rebaseActivityOverrideConfig ---- activityResource.overrideConfig = {0.0 ?mcc?mnc ?localeList ?layoutDir ?swdp ?wdp ?hdp ?density ?lsize ?long ?ldr ?wideColorGamut ?orien ?uimode ?night ?touch ?keyb/?/? ?nav/? winConfig={ mBounds=Rect(0, 0 - 0, 0) mAppBounds=null mMaxBounds=Rect(0, 0 - 0, 0) mWindowingMode=undefined mDisplayWindowingMode=undefined mActivityType=undefined mAlwaysOnTop=undefined mRotation=undefined} ?fontWeightAdjustment?ecid} rebasedOverrideConfig = {0.93 ?mcc?mnc [en_US] ldltr sw360dp w360dp h722dp 480dpi nrml long port finger -keyb/v/h -nav/h winConfig={ mBounds=Rect(0, 0 - 1080, 2400) mAppBounds=Rect(0, 90 - 1080, 2256) mMaxBounds=Rect(0, 0 - 1080, 2400) mWindowingMode=fullscreen mDisplayWindowingMode=fullscreen mActivityType=standard mAlwaysOnTop=undefined mRotation=ROTATION_0} s.3 fontWeightAdjustment=0?ecid}
12-17 11:27:34.370 7133 7133 I ukynho_res: ResourcesManager#rebaseActivityOverrideConfig ---- newKey = ResourcesKey{ mHash=7d7a14ea mOverrideConfig={0.93 ?mcc?mnc [en_US] ldltr sw360dp w360dp h722dp 480dpi nrml long port finger -keyb/v/h -nav/h winConfig={ mBounds=Rect(0, 0 - 1080, 2400) mAppBounds=Rect(0, 90 - 1080, 2256) mMaxBounds=Rect(0, 0 - 1080, 2400) mWindowingMode=fullscreen mDisplayWindowingMode=fullscreen mActivityType=standard mAlwaysOnTop=undefined mRotation=ROTATION_0} s.3 fontWeightAdjustment=0?ecid}} rebasedOverrideConfig = {0.93 ?mcc?mnc [en_US] ldltr sw360dp w360dp h722dp 480dpi nrml long port finger -keyb/v/h -nav/h winConfig={ mBounds=Rect(0, 0 - 1080, 2400) mAppBounds=Rect(0, 90 - 1080, 2256) mMaxBounds=Rect(0, 0 - 1080, 2400) mWindowingMode=fullscreen mDisplayWindowingMode=fullscreen mActivityType=standard mAlwaysOnTop=undefined mRotation=ROTATION_0} s.3 fontWeightAdjustment=0?ecid}
这里Resources@ca21027对应的ActivityResource.overrideConfig为Configuration.EMPTY,那么newKey的创建就完全使用了ResourcesManager#rebaseActivityOverrideConfig方法传入的newOverrideConfig。
12-17 11:27:34.371 7133 7133 I ukynho_res: ResourcesManager#findOrCreateResourcesImplForKeyLocked ---- key = ResourcesKey{ mHash=7d7a14ea mOverrideConfig={0.93 ?mcc?mnc [en_US] ldltr sw360dp w360dp h722dp 480dpi nrml long port finger -keyb/v/h -nav/h winConfig={ mBounds=Rect(0, 0 - 1080, 2400) mAppBounds=Rect(0, 90 - 1080, 2256) mMaxBounds=Rect(0, 0 - 1080, 2400) mWindowingMode=fullscreen mDisplayWindowingMode=fullscreen mActivityType=standard mAlwaysOnTop=undefined mRotation=ROTATION_0} s.3 fontWeightAdjustment=0?ecid}}
12-17 11:27:34.372 7133 7133 I ukynho_res: ResourcesManager#findOrCreateResourcesImplForKeyLocked ---- existing resourcesIml = android.content.res.ResourcesImpl@7863c28
12-17 11:27:34.372 7133 7133 I ukynho_res: ResourcesManager#updateResourcesForActivity ---- i = 0 resourcesImpl = android.content.res.ResourcesImpl@7863c28 resources.getImpl() = android.content.res.ResourcesImpl@ff5ba1
12-17 11:27:34.373 7133 7133 I ukynho_res: Resources#setImpl ---- resources = android.content.res.Resources@ca21027 mResourcesImpl = android.content.res.ResourcesImpl@ff5ba1 impl = android.content.res.ResourcesImpl@7863c28
最终创建了一个新的ResourcesKey{ mHash=3f55d5d ......},但是这里在ResourcesImpl.mResourceImpls中可以找到一个对应的ResourcesImpl对象,ResouresImpl@7863c28,最后通过Resources#setImpl方法将Resources@ca21027中的mResourcesImpl成员赋值为ResouresImpl@7863c28,完成了此次的Configuration更新。
| Context | Resources | ResourcesImpl | ResourcesKey |
|---|---|---|---|
| ContextImpl@8a62ae | Resources@dfa5116 | ||
| ContextImpl@a9dfbba | Resources@82d2a74 | ResourcesImpl@765ba61 | ResourcesKey{ mHash=593d1817 ......} |
| ContextImpl@2c79e0 | Resources@82d2a74 | ResourcesImpl@765ba61 | ResourcesKey{ mHash=593d1817 ......} |
| ContextImpl@256d33f | Resources@82d2a74 | ResourcesImpl@765ba61 | ResourcesKey{ mHash=593d1817 ......} |
| ContextImpl@763a4f8 | Resources@c3ad9c2 | ResourcesImpl@ab5eb37 | ResourcesKey{ mHash=2a5c3093......} |
| ContextImpl@df10a2f | Resources@ca21027 | ResouresImpl@7863c28 | ResourcesKey{ mHash=7d7a14ea......} |
| ContextImpl@d7e7b3 | Resources@8df4c0f | ResourcesImpl@765ba61 | ResourcesKey{ mHash=593d1817 ......} |
| Resources@19dc86c | ResourcesImpl@765ba61 | ResourcesKey{ mHash=593d1817 ......} | |
| ResourcesImpl@ff5ba1 | ResourcesKey{ mHash=3f55d5d......} |
4 总结
4.1 ResourcesManager#applyConfigurationToResource
ResourcesManager#applyConfigurationToResources中,更新的是所有ResourcesImpl(因为Resources只是对ResourcesImpl的一层封装,真正持有Configuration的是ResourcesImpl),通过遍历ResourcesImpl.mResourceImpls,来更新每一个ResourcesImpl对象。
finalConfig = newConfig.updateFrom(key,mOverrideConfiguration)。
其中newConfig为从系统服务进程传过来的的Configuration,key为当前ResourcesImpl对象对应的ResourcesKey对象,得到最终的finalConfig,通过ResourcesImpl#updateConfiguration方法,来更新ResourcesImpl中的mConfiguration对象。
如果一个ResourcesImpl对应的ResourcesKey不为Configuration.EMPTY,那么在一次Configuration的更新中,finalConfig的某些属性很有可能被key.mOverrideConfiguration覆盖掉。
4.2 ResourcesManager#updateResourcesForActivity
ResourcesManager#updateResourcesForActivity,更新的是当前Activity对应的所有Resources,那么就需要知道哪些Resources是对应当前Activity的。根据之前的分析,我们知道每一个Activity都对应一个ActivityResources对象,ActivityResources.activityResources队列中的每一个ActivityResource都对应一个Resources对象,这些Resources对象就是与当前Activity关联的Resources对象。那么我们就可以找到当前Activity对应的ActivityResources对象,通过遍历ActivityResoures.activityResources,更新每一个ActivityResource对象中保存的Resources对象。
更新每一个Resoures对象的具体内容是,以本次ResourcesManager#updateResourcesForActivity中从系统服务端传入的newConfig为baseConfig,再update上创建Resources时候传入的初始Configuration(已经保存在ActivityResource.overrideConfig中),生成一个finalConfig,然后基于这个finalConfig生成一个新的ResourcesKey。
finalConfig = newConfig.updateFrom(ActivityResoruce,overrideConfig)。
最终有两种情况:
1)、生成的这个ResourcesKey,在ResourcesManager.mResourcesImpl中找不到对应的ResourcesImpl对象,那么创建一个新的ResourcesImpl对象,并且将键值对<ResourcesKey, ResourcesImpl>加入到ResourcesManager.mResourcesImpl中,返回这个新创建的ResourcesImpl对象。
2)、生成的这个ResourcesKey,在ResourcesManager.mResourcesImpl中可以找到对应的ResourcesImpl对象,那么直接返回这个ResourcesImpl对象。
最终通过Resources#setImpl,通过替换Resources中的mResourcesImpl成员完成当前Resources的更新。
4.3 Log分析
从Log分析这一节中,可以看到ResourcesManager存在ResourcesImpl对象的复用。
我们只看和Resources@ca21027的部分:
1)、Activity创建的时候,生成了Resources@ca21027,以及<ResourcesKey{ mHash=7d7a14ea......}, ResouresImpl@7863c28>。
2)、从竖屏转为横屏,生成了新的键值对,<ResourcesKey{ mHash=3f55d5d......}, ResourcesImpl@ff5ba1 >,Resources@ca21027将成员变量mResourcesImpl从ResouresImpl@7863c28替换为ResourcesImpl@ff5ba1。
3)、从横屏转为竖屏,这个时候,没有创建新的键值对,而是复用了第一步的<ResourcesKey{ mHash=7d7a14ea......}, ResouresImpl@7863c28>,Resources@ca21027将成员变量mResourcesImpl从ResouresImpl@ff5ba1替换为ResourcesImpl@7863c28。
4)、同样,再从竖屏转为横屏,也不会创建新的键值对,会复用第二步生成的<ResourcesKey{ mHash=3f55d5d......}, ResourcesImpl@ff5ba1>,Resources@ca21027将成员变量mResourcesImpl从ResouresImpl@7863c28替换为ResourcesImpl@ff5ba1。