Resources简介
Resources是FrameWork层提供给用户访问应用资源的类。这个类是在应用的AssetManager之上,并且提供了高层Api让用户能够从资源中获取各类型数据。
Android 资源系统会一直追踪所有跟Application关联的无代码资源。你可以通过android.content.Context#getResources getResources()获取Application 相关的资源Resources,来使用我们的Application资源。
下面我们将了解下Resources的加载流程,首先看下他们的关系图
Resources 加载流程
ContextImpl的创建
static ContextImpl createActivityContext(ActivityThread mainThread,
LoadedApk packageInfo, ActivityInfo activityInfo, IBinder activityToken, int displayId,
Configuration overrideConfiguration) {
//1.传教ContextImpl 实例
ContextImpl context = new ContextImpl(null, mainThread, packageInfo, activityInfo.splitName,
activityToken, null, 0, classLoader);
// Clamp display ID to DEFAULT_DISPLAY if it is INVALID_DISPLAY.
displayId = (displayId != Display.INVALID_DISPLAY) ? displayId : Display.DEFAULT_DISPLAY;
final CompatibilityInfo compatInfo = (displayId == Display.DEFAULT_DISPLAY)
? packageInfo.getCompatibilityInfo()
: CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO;
//2.获取ResourcesManager 实例,用来创建Resources
final ResourcesManager resourcesManager = ResourcesManager.getInstance();
// Create the base resources for which all configuration contexts for this Activity
// will be rebased upon.
//3.创建resources并设置给给当前Activity的context
context.setResources(resourcesManager.createBaseActivityResources(activityToken,
packageInfo.getResDir(),
splitDirs,
packageInfo.getOverlayDirs(),
packageInfo.getApplicationInfo().sharedLibraryFiles,
displayId,
overrideConfiguration,
compatInfo,
classLoader));
context.mDisplay = resourcesManager.getAdjustedDisplay(displayId,
context.getResources());
return context;
}
在ContextImpl类中,静态方法创建ContextImpl实例时会给ContextImpl实例设置Resources,而创建Resources的工作就交给我们的ResourcesManager来完成
ResourcesManager
在ResourcesManager中有几个成员变量很重要,所以这里先讲下
ResourcesManager成员变量
//每个相同tokn的Activity都有一个基本Config对应多个资源Resources,而资源对象又可能指定了它们自己的config。
private final WeakHashMap<IBinder, ActivityResources> mActivityResourceReferences =
new WeakHashMap<>();
//存放ResourcesImpl实例,因为ResourcesImpl是一个很有重量级的对象,所以需要重复使用
private final ArrayMap<ResourcesKey, WeakReference<ResourcesImpl>> mResourceImpls =
new ArrayMap<>();
//Resources 资源列表,存放跟activity 无关联的资源,例如Application的Resources
private final ArrayList<WeakReference<Resources>> mResourceReferences = new ArrayList<>();
ActivityResources
可以从数据类型上看出来,Activity和Resources关系是一对多,每个相同token的Activity可能有不同的Config所以对应多个Resourcess
private static class ActivityResources {
public final Configuration overrideConfig = new Configuration();
public final ArrayList<WeakReference<Resources>> activityResources = new ArrayList<>();
}
ResourcesKey
用于ResourcesImpl实例一一对应的key,ResourcesKey比对的是资源路径,Configuration 等参数
public ResourcesKey(@Nullable String resDir,
@Nullable String[] splitResDirs,
@Nullable String[] overlayDirs,
@Nullable String[] libDirs,
int displayId,
@Nullable Configuration overrideConfig,
@Nullable CompatibilityInfo compatInfo) {
mResDir = resDir;
mSplitResDirs = splitResDirs;
mOverlayDirs = overlayDirs;
mLibDirs = libDirs;
mDisplayId = displayId;
mOverrideConfiguration = new Configuration(overrideConfig != null
? overrideConfig : Configuration.EMPTY);
mCompatInfo = compatInfo != null ? compatInfo : CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO;
int hash = 17;
hash = 31 * hash + Objects.hashCode(mResDir);
hash = 31 * hash + Arrays.hashCode(mSplitResDirs);
hash = 31 * hash + Arrays.hashCode(mOverlayDirs);
hash = 31 * hash + Arrays.hashCode(mLibDirs);
hash = 31 * hash + mDisplayId;
hash = 31 * hash + Objects.hashCode(mOverrideConfiguration);
hash = 31 * hash + Objects.hashCode(mCompatInfo);
mHash = hash;
}
ResourcesImpl
存放在ResourcesManager中的mResourceImpls局部变量中,这是一个重量级的对象,所以必须要高复用,他跟ResourcesKey相关联
Resources 创建流程
ContextImpl创建时会调用ResourcesManager中的createBaseActivityResources创建Resources
public @Nullable Resources createBaseActivityResources(@NonNull IBinder activityToken,
@Nullable String resDir,
@Nullable String[] splitResDirs,
@Nullable String[] overlayDirs,
@Nullable String[] libDirs,
int displayId,
@Nullable Configuration overrideConfig,
@NonNull CompatibilityInfo compatInfo,
@Nullable ClassLoader classLoader) {
try {
Trace.traceBegin(Trace.TRACE_TAG_RESOURCES,
"ResourcesManager#createBaseActivityResources");
//1.根据资源路径等相关属性参数创建 ResourcesKey
final ResourcesKey key = new ResourcesKey(
resDir,
splitResDirs,
overlayDirs,
libDirs,
displayId,
overrideConfig != null ? new Configuration(overrideConfig) : null, // Copy
compatInfo);
classLoader = classLoader != null ? classLoader : ClassLoader.getSystemClassLoader();
synchronized (this) {
// Force the creation of an ActivityResourcesStruct.
//2.根据activityToken 创建对应ActivityResources(如果没有创建过)
getOrCreateActivityResourcesStructLocked(activityToken);
}
// Update any existing Activity Resources references.
//3.更新AcitivityResources资源
updateResourcesForActivity(activityToken, overrideConfig, displayId,
false /* movedToDifferentDisplay */);
// Now request an actual Resources object.
//4.获取或创建资源
return getOrCreateResources(activityToken, key, classLoader);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
}
}
getOrCreateActivityResourcesStructLocked
非常简单的逻辑,从mActivityResourceReferences成员变量中取缓存,如果没有就创建新的实例放到缓存中
private ActivityResources getOrCreateActivityResourcesStructLocked(
@NonNull IBinder activityToken) {
ActivityResources activityResources = mActivityResourceReferences.get(activityToken);
if (activityResources == null) {
activityResources = new ActivityResources();
mActivityResourceReferences.put(activityToken, activityResources);
}
return activityResources;
}
updateResourcesForActivity
public void updateResourcesForActivity(@NonNull IBinder activityToken,
@Nullable Configuration overrideConfig, int displayId,
boolean movedToDifferentDisplay) {
try {
Trace.traceBegin(Trace.TRACE_TAG_RESOURCES,
"ResourcesManager#updateResourcesForActivity");
synchronized (this) {
//获取缓存的activityResources
final ActivityResources activityResources =
getOrCreateActivityResourcesStructLocked(activityToken);
if (Objects.equals(activityResources.overrideConfig, overrideConfig)
&& !movedToDifferentDisplay) {
//它们是相同的,没有显示ID的变化,没有工作要做。
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);
// 更新activityResources 的 base Config
if (overrideConfig != null) {
activityResources.overrideConfig.setTo(overrideConfig);
} else {
activityResources.overrideConfig.unset();
}
final boolean activityHasOverrideConfig =
!activityResources.overrideConfig.equals(Configuration.EMPTY);
// Rebase each Resources associated with this Activity.
//重定这个activity 相关联的所有资源
final int refCount = activityResources.activityResources.size();
for (int i = 0; i < refCount; i++) {
WeakReference<Resources> weakResRef = activityResources.activityResources.get(
i);
Resources resources = weakResRef.get();
if (resources == null) {
continue;
}
//从mResourceImpls中获取对应ResourceImpl的key
final ResourcesKey oldKey = findKeyForResourceImplLocked(resources.getImpl());
if (oldKey == null) {
Slog.e(TAG, "can't find ResourcesKey for resources impl="
+ resources.getImpl());
continue;
}
// Build the new override configuration for this ResourcesKey.
final Configuration rebasedOverrideConfig = new Configuration();
if (overrideConfig != null) {
rebasedOverrideConfig.setTo(overrideConfig);
}
if (activityHasOverrideConfig && oldKey.hasOverrideConfiguration()) {
// Generate a delta between the old base Activity override configuration and
// the actual final override configuration that was used to figure out the
// real delta this Resources object wanted.
Configuration overrideOverrideConfig = Configuration.generateDelta(
oldConfig, oldKey.mOverrideConfiguration);
rebasedOverrideConfig.updateFrom(overrideOverrideConfig);
}
//根据新的config displayId 等参数创建新的key
final ResourcesKey newKey = new ResourcesKey(oldKey.mResDir,
oldKey.mSplitResDirs,
oldKey.mOverlayDirs, oldKey.mLibDirs, displayId,
rebasedOverrideConfig, oldKey.mCompatInfo);
//根据新key从mResourceImpls中获取缓存ResourcesImpl
ResourcesImpl resourcesImpl = findResourcesImplForKeyLocked(newKey);
if (resourcesImpl == null) {
//5.根据新key创建新的ResourcesImpl
resourcesImpl = createResourcesImpl(newKey);
if (resourcesImpl != null) {
mResourceImpls.put(newKey, new WeakReference<>(resourcesImpl));
}
}
//将Resources和ResourcesImpl 关联上
if (resourcesImpl != null && resourcesImpl != resources.getImpl()) {
resources.setImpl(resourcesImpl);
}
}
}
} finally {
Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
}
}
通过上面代码逻辑可以看出,同一个activityToken的ActivityResources会将里的所有resources关联上同一个ResourcesImpl实例。
getOrCreateResources
private @Nullable Resources getOrCreateResources(@Nullable IBinder activityToken,
@NonNull ResourcesKey key, @NonNull ClassLoader classLoader) {
synchronized (this) {
.....省略复用 ResourcesImpl 代码
//如果执行到这一步,说明没有创建ResourcesImpl
ResourcesImpl resourcesImpl = createResourcesImpl(key);
if (resourcesImpl == null) {
return null;
}
//resourcesImpl存入缓存
mResourceImpls.put(key, new WeakReference<>(resourcesImpl));
final Resources resources;
if (activityToken != null) {
//从mActivityResourceReferences缓存取,没有就添加新的Resources到ActivityResource缓存中
resources = getOrCreateResourcesForActivityLocked(activityToken, classLoader,
resourcesImpl, key.mCompatInfo);
} else {
//从 mResourceReferences 缓存取,没有就添加新的Resources到mResourceReferences缓存中
resources = getOrCreateResourcesLocked(classLoader, resourcesImpl, key.mCompatInfo);
}
return resources;
}
}
AssetManager 创建过程
在我们开始讲解 AssetManager 创建的过程之前我们先看下 ResourcesManager 中 与 AssetManager相关的局部变量
ResourcesManager 成员变量
/**
* The ApkAssets we are caching and intend to hold strong references to.
* 这里使用LruCache缓存策略,缓存最近使用最频繁3个ApkAssets
*/
private final LruCache<ApkKey, ApkAssets> mLoadedApkAssets = new LruCache<>(3);
/**
* The ApkAssets that are being referenced in the wild that we can reuse, even if they aren't
* in our LRU cache. Bonus resources :)
* 这个是对 mLoadedApkAssets 缓存的补充,当有超过3个 ApkAssets 需要缓存时这里会缓存超出限制的 ApkAssets
*/
private final ArrayMap<ApkKey, WeakReference<ApkAssets>> mCachedApkAssets = new ArrayMap<>();
ApkKey 用于ApkAssets实例一一对应的key。
ResourcesManager createResourcesImpl 创建ResourcesImpl
private @Nullable ResourcesImpl createResourcesImpl(@NonNull ResourcesKey key) {
final DisplayAdjustments daj = new DisplayAdjustments(key.mOverrideConfiguration);
daj.setCompatibilityInfo(key.mCompatInfo);
//根据ResourcesKey创建AssetManager
final AssetManager assets = createAssetManager(key);
if (assets == null) {
return null;
}
final DisplayMetrics dm = getDisplayMetrics(key.mDisplayId, daj);
final Configuration config = generateConfig(key, dm);
//创建ResourcesImpl实例与
final ResourcesImpl impl = new ResourcesImpl(assets, dm, config, daj);
return impl;
}
从上面创建的代码中我们可以看出ResourcesImpl和AssetManager时一一对应的关系
createAssetManager 创建AssetManager
@VisibleForTesting
protected @Nullable AssetManager createAssetManager(@NonNull final ResourcesKey key) {
final AssetManager.Builder builder = new AssetManager.Builder();
// resDir can be null if the 'android' package is creating a new Resources object.
// This is fine, since each AssetManager automatically loads the 'android' package
// already.
if (key.mResDir != null) {
try {
builder.addApkAssets(loadApkAssets(key.mResDir, false /*sharedLib*/,
false /*overlay*/));
} catch (IOException e) {
return null;
}
}
if (key.mSplitResDirs != null) {
for (final String splitResDir : key.mSplitResDirs) {
try {
builder.addApkAssets(loadApkAssets(splitResDir, false /*sharedLib*/,
false /*overlay*/));
} catch (IOException e) {
return null;
}
}
}
if (key.mOverlayDirs != null) {
for (final String idmapPath : key.mOverlayDirs) {
try {
builder.addApkAssets(loadApkAssets(idmapPath, false /*sharedLib*/,
true /*overlay*/));
} catch (IOException e) {
}
}
}
if (key.mLibDirs != null) {
for (final String libDir : key.mLibDirs) {
if (libDir.endsWith(".apk")) {
// Avoid opening files we know do not have resources,
// like code-only .jar files.
try {
builder.addApkAssets(loadApkAssets(libDir, true /*sharedLib*/,
false /*overlay*/));
} catch (IOException e) {
}
}
}
}
return builder.build();
}
通过AssetManager Build 设计模式,创建AssetManager实例。
loadApkAssets 加载ApkAssets
private @NonNull ApkAssets loadApkAssets(String path, boolean sharedLib, boolean overlay)
throws IOException {
//生成ApkKey
final ApkKey newKey = new ApkKey(path, sharedLib, overlay);
//从LRU mLoadedApkAssets缓存中取
ApkAssets apkAssets = mLoadedApkAssets.get(newKey);
if (apkAssets != null) {
return apkAssets;
}
// Optimistically check if this ApkAssets exists somewhere else.
//从 mCachedApkAssets 取缓存
final WeakReference<ApkAssets> apkAssetsRef = mCachedApkAssets.get(newKey);
if (apkAssetsRef != null) {
apkAssets = apkAssetsRef.get();
if (apkAssets != null) {
//刷新mLoadedApkAssets缓存
mLoadedApkAssets.put(newKey, apkAssets);
return apkAssets;
} else {
// Clean up the reference.
// 不存在就清楚缓存
mCachedApkAssets.remove(newKey);
}
}
// We must load this from disk.
// 缓存中都没取到 从磁盘中加载 ApkAssets
if (overlay) {
apkAssets = ApkAssets.loadOverlayFromPath(overlayPathToIdmapPath(path),
false /*system*/);
} else {
apkAssets = ApkAssets.loadFromPath(path, false /*system*/, sharedLib);
}
// 缓存ApkAssets
mLoadedApkAssets.put(newKey, apkAssets);
mCachedApkAssets.put(newKey, new WeakReference<>(apkAssets));
return apkAssets;
}
小结
- ResourcesManager 负责整个的加载逻辑控制,从Resources 到 AssetsManager创建流程。
- app通过Resources获取资源,最终是通过 AssetsManager调用native方法来实现的。
- AssetsManager是个重量级对象跟他相关联的对象ResourcesImpl 需要慎用,内存损耗较大