在 Android 开发中我们使用 Resources
来获取 res
目录下的各种与设备相关的资源。而使用 AssetManager
来获取 assets
目录下的资源。
一般来说 Resources
对象通过 Context
获得。
appContext
中的 resources
是在创建之后通过如下代码设置。
context.setResources(packageInfo.getResources());
而 LoadedApk
中则是通过如下代码创建:
mResources = ResourcesManager.getInstance().getResources(null, mResDir,
splitPaths, mOverlayDirs, mApplicationInfo.sharedLibraryFiles,
Display.DEFAULT_DISPLAY, null, getCompatibilityInfo(),
getClassLoader());
其中 mResDir 对应 ApplicationInfo.sourceDir 字段
/**
* Full path to the base APK for this application.
*/
public String sourceDir;
ResourcesManager.getInstance().getResources
的主要逻辑如下:
final ResourcesKey key = new ResourcesKey(
resDir,
splitResDirs,
overlayDirs,
libDirs,
displayId,
overrideConfig != null ? new Configuration(overrideConfig) : null, // Copy
compatInfo);
classLoader = classLoader != null ? classLoader : ClassLoader.getSystemClassLoader();
return getOrCreateResources(activityToken, key, classLoader);
getOrCreateResources
主要逻辑如下:
// 1) 先创建 ResourcesImpl
ResourcesImpl resourcesImpl = createResourcesImpl(key);
// 2) 再创建 Resources
resources = getOrCreateResourcesLocked(classLoader, resourcesImpl, key.mCompatInfo);
createResourcewImpl
主要逻辑如下:
final AssetManager assets = createAssetManager(key);
if (assets == null) {
return null;
}
final DisplayMetrics dm = getDisplayMetrics(key.mDisplayId, daj);
final Configuration config = generateConfig(key, dm);
final ResourcesImpl impl = new ResourcesImpl(assets, dm, config, daj);
return impl;
其中 createAssetManager(key)
的主要逻辑如下:
// 1) 创建 assets 对象实例。
AssetManager assets = new AssetManager();
// 2) 添加各资源目录。
assets.addAssetPath(key.mResDir)
assets.addAssetPath(splitResDir)
assets.addOverlayPath(idmapPath);
assets.addAssetPathAsSharedLibrary(libDir)
最终 Resources
的创建逻辑如下:
// getOrCreateResourcesLocked
Resources resources = compatInfo.needsCompatResources() ? new CompatResources(classLoader)
: new Resources(classLoader);
resources.setImpl(impl);
以 Resouces.getString 来查看资源的读取流程
1)调用 getText
public String getString(@StringRes int id) throws NotFoundException {
return getText(id).toString();
}
- 通过
resourcesImpl
调用AssetsManager
的getResourceText
@NonNull public CharSequence getText(@StringRes int id) throws NotFoundException {
CharSequence res = mResourcesImpl.getAssets().getResourceText(id);
if (res != null) {
return res;
}
throw new NotFoundException("String resource ID #0x"
+ Integer.toHexString(id));
}
getResourceText
@Nullable
final CharSequence getResourceText(@StringRes int resId) {
synchronized (this) {
final TypedValue outValue = mValue;
if (getResourceValue(resId, 0, outValue, true)) {
return outValue.coerceToString();
}
return null;
}
}
getResourceValue
final boolean getResourceValue(@AnyRes int resId, int densityDpi, @NonNull TypedValue outValue,
boolean resolveRefs) {
synchronized (this) {
final int block = loadResourceValue(resId, (short) densityDpi, outValue, resolveRefs);
if (block < 0) {
return false;
}
// Convert the changing configurations flags populated by native code.
outValue.changingConfigurations = ActivityInfo.activityInfoConfigNativeToJava(
outValue.changingConfigurations);
if (outValue.type == TypedValue.TYPE_STRING) {
outValue.string = mStringBlocks[block].get(outValue.data);
}
return true;
}
}
最后通过 loadResourceValue
这一原生方法读取。
/** Returns true if the resource was found, filling in mRetStringBlock and
* mRetData. */
private native final int loadResourceValue(int ident, short density, TypedValue outValue,
boolean resolve);
一点小总结
资源的加载的主要底层逻辑还是由 AssetsManager
来处理的。
一些动态主题包的实现原理主要是通过。AssetsManager.addAssetPath(String path)
这一接口来创建对应主题包的 Resources
对象来实现的。
网友评论