其他
浅析Android 资源加载,一眼懂!
https://juejin.cn/post/7388441764874813459
// ContextImpl.java 文件
// 静态方法,创建 Application 的上下文的实现类 ContextImpl 对象
static ContextImpl createAppContext(ActivityThread mainThread, LoadedApk packageInfo ...) {
if (packageInfo == null) throw new IllegalArgumentException("packageInfo");
// 创建 ContextImpl 对象
ContextImpl context = new ContextImpl(null, mainThread, packageInfo,
ContextParams.EMPTY, null, null, null, null, null, 0, null, opPackageName);
// 创建 Resources 对象,并赋值给 ContextImpl
context.setResources(packageInfo.getResources());
...
return context;
}
// 将 resources 赋值给 ContextImpl 的 mResources 对象
void setResources(Resources r) {
...
mResources = r;
}
// LoadedApk.java
public Resources getResources() {
if (mResources == null) {
...
// 创建 Resources
mResources = ResourcesManager.getInstance().getResources(null, mResDir...);
}
return mResources;
}
// ResourcesManager.java
public Resources getResources(IBinder activityToken, String resDir...) {
// 创建 key,用于缓存 resources 对象
final ResourcesKey key = new ResourcesKey(resDir ...);
...
// 创建 APK 加载资源的内存表示,同时防止GC回收,用于 C++ 交互
final ApkAssetsSupplier assetsSupplier = createApkAssetsSupplierNotLocked(key);
...
// 创建 Resources
Resources resources = createResources(key, classLoader, assetsSupplier);
return resources;
}
// 创建 Resources
private Resources createResources(ResourcesKey key, ClassLoader classLoader, ApkAssetsSupplier apkSupplier) {
synchronized (mLock) {
...
// 1、创建/获取 ResourcesImpl 对象
ResourcesImpl resourcesImpl = findOrCreateResourcesImplForKeyLocked(key, apkSupplier);
if (resourcesImpl == null) {
return null;
}
// 2、创建/获取 Resources 对象
return createResourcesLocked(classLoader, resourcesImpl, key.mCompatInfo);
}
}
/**=======================创建 Resources 分为两部分=========================**/
// 第一部分,创建 ResourceImpl 的流程
// 获取/创建 ResourceImpl
private ResourcesImpl findOrCreateResourcesImplForKeyLocked(ResourcesKey key, ApkAssetsSupplier apkSupplier) {
// 1.1、从缓存加载 ResourcesImpl
ResourcesImpl impl = findResourcesImplForKeyLocked(key);
if (impl == null) {
// 1.2、创建 ResourcesImpl
impl = createResourcesImpl(key, apkSupplier);
if (impl != null) {
// 1.3、将创建的 ResourcesImpl 对象放入缓存
mResourceImpls.put(key, new WeakReference<>(impl));
}
}
return impl;
}
// 创建 ResourcesImpl
private ResourcesImpl createResourcesImpl(ResourcesKey key, ApkAssetsSupplier apkSupplier) {
// 1.2.1、创建 AssetManager
final AssetManager assets = createAssetManager(key, apkSupplier);
if (assets == null) {
return null;
}
// 1.2.2、创建 ResourceImpl
final ResourcesImpl impl = new ResourcesImpl(assets, displayMetrics, config, daj);
return impl;
}
// 第二部分,创建 Resources 的流程
private Resources createResourcesLocked(... ResourcesImpl impl,CompatibilityInfo compatInfo) {
// 2.1、创建 Resources 对象
Resources resources = compatInfo.needsCompatResources() ? new CompatResources(classLoader)
: new Resources(classLoader);
// 2.2、将 impl 设置到 Resources 内
resources.setImpl(impl);
// 2.3、将 resources 放入缓存内
mResourceReferences.add(new WeakReference<>(resources, mResourcesReferencesQueue));
...
return resources;
}
创建 ContextImpl 时,同步会创建相关的 Resources 对象,并将 Resources 赋值给 mResources 对象; 通过 ResourcesManager 的 getResources 方法创建 Resources 对象; 创建 ResourcesKey 对象,作为缓存key和记录 Resource 相关的信息; 创建 ApkAssetsSupplier 对象,内部维护了一个 Map 集合,用于缓存不同目录下的资源数据,Key 为 Appkey(目录path生成),Value 为 ApkAssets 的 Map; 创建 AssetManager,用于和 native 层进行交互; 创建 ResourceImpl,作为 Resources 的实现类; 创建 Resources,并将 resources 设置到 ContextImpl;
// Context.java
public final CharSequence getText(@StringRes int resId) {
// 通过 resources 对资源进行加载
return getResources().getText(resId);
}
// Resources.java
public CharSequence getText(@StringRes int id) throws NotFoundException {
// 通过 ResourcesImpl 内的 AssetManager 对资源进行加载
CharSequence res = mResourcesImpl.getAssets().getResourceText(id);
if (res != null) {
return res;
}
}
// AssetManager.java
CharSequence getResourceText(@StringRes int resId) {
synchronized (this) {
// java 对象,用于接收 native 层加载结果
final TypedValue outValue = mValue;
if (getResourceValue(resId, 0, outValue, true)) {
return outValue.coerceToString();
}
return null;
}
}
boolean getResourceValue(int resId, int densityDpi, TypedValue outValue ...) {
synchronized (this) {
// native 加载资源
final int cookie = nativeGetResourceValue(
mObject, resId, (short) densityDpi, outValue, resolveRefs);
// 资源未找到
if (cookie <= 0) {
return false;
}
if (outValue.type == TypedValue.TYPE_STRING) {
// 对 string 数据进行解析
if ((outValue.string = getPooledStringForCookie(cookie, outValue.data)) == null) {
return false;
}
}
return true;
}
}
通过 Resources 提供的加载不同资源的方法,将id传入开始加载资源; ResourcesImpl 内则是通过 AssetManager 真正去加载资源; AssetManager 创建 java 层接收数据的 TypeValue 对象,并将其传入 native 层; 通过 native 方法 nativeGetResourceValue,开始加载id对应的资源数据,并存入 TypeValue 对象内; 解析 TypeValue 内的数据是否合法,并将数据转换为 java string;