资源初始化整体架构
Android 应用程序资源初始化/资源加载,主要包括以下的几个系统的组件,如 ContextImpl, ResourcesImpl,Java 层和 Native 层的 AssetManager 和 ApkAssets 等。它们之间的关系如下:
ContextImpl是应用程序加载资源的入口ContextImpl会间接持有AssetManager,而AssetManager会持有/管理多个ApkAssets, 其中 app 进程本身所加载的 apk 的ApkAssets,也会包含系统的资源包 (framework-res.apk) 的ApkAssets, 如果有 RRO (Runtime Resources Overlay) apk 覆盖这个 apk 的话,还会包含在 RRO Apk 的 ApkAssetApkAssets标识一个 Apk 所有的资源集合, 包括 res/ , assets/ 和 resources.arsc
资源初始化流程分析
创建 ContextImpl
Android App 的资源初始化发生 Activity 和 Service 等组件创建之前,具体发生在 handleBindApplication, 在这个过程中会创建 ContextImpl
private void handleBindApplication(AppBindData data) {
...
// getPackageInfo 会创建一个 LoadedApk 对象,并且赋值给 data.info
data.info = getPackageInfo(data.appInfo, mCompatibilityInfo, null /* baseLoader */,
false /* securityViolation */, true /* includeCode */,
false /* registerPackage */, isSdkSandbox);
...
// 使用上一步创建的 LoadedApk 对象实例化一个 ContextImpl
final ContextImpl appContext = ContextImpl.createAppContext(this, data.info);
...
}
在 handleBindApplication 中, 通过 getPackageInfo 获取到 App 的安装信息,然后再创建 ContextImpl 这个对象,接下来继续分析 getPackageInfo
创建 LoadedApk
private LoadedApk getPackageInfo(ApplicationInfo aInfo, CompatibilityInfo compatInfo,
ClassLoader baseLoader, boolean securityViolation, boolean includeCode,
boolean registerPackage, boolean isSdkSandbox) {
final boolean differentUser = (UserHandle.myUserId() != UserHandle.getUserId(aInfo.uid));
synchronized (mResourcesManager) {
...
LoadedApk packageInfo = ref != null ? ref.get() : null;
...
// LoaderApk 对象包含了当前加载的 apk 的所有信息,如 mPackageName,mApplicationInfo,mOverlayPaths, mBaseClassLoader 等等
packageInfo =
new LoadedApk(this, aInfo, compatInfo, baseLoader,
securityViolation, includeCode
&& (aInfo.flags & ApplicationInfo.FLAG_HAS_CODE) != 0, registerPackage);
...
return packageInfo;
}
}
getPackageInfo 中创建了一个 LoadedApk 对象,这个对象包含了当前 apk 的所有安装信息,如 mPackageName,mApplicationInfo,mOverlayPaths, mBaseClassLoader 等等。接下来创建 ContextImpl,并且把 LoadedApk 作为参数传入。
static ContextImpl createAppContext(ActivityThread mainThread, LoadedApk packageInfo) {
// 真正创建 ContextImpl
ContextImpl context = new ContextImpl(null, mainThread, packageInfo,
ContextParams.EMPTY, null, null, null, null, null, 0, null, opPackageName,
DEVICE_ID_DEFAULT, false);
// 通过 LoadedApk 去获取资源,并且由 ContextImpl 持有这个资源
context.setResources(packageInfo.getResources());
return context;
}
- 通过 LoadedApk.getResources 来加载 Resources
- ContextImpl 会持有加载之后的 Resources
接下来看 LoadedApk.getResources
创建 ResourcesImpl
public Resources getResources() {
if (mResources == null) {
mResources = ResourcesManager.getInstance().getResources(null, mResDir,
splitPaths, mLegacyOverlayDirs, mOverlayPaths,
mApplicationInfo.sharedLibraryFiles, null, null, getCompatibilityInfo(),
getClassLoader(), null);
}
return mResources;
}
LoadedApk.getResources 会 ResourcesManager 的单例调用 getResources
@Nullable
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) {
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]));
classLoader = classLoader != null ? classLoader : ClassLoader.getSystemClassLoader();
// Preload the ApkAssets required by the key to prevent performing heavy I/O while the
// ResourcesManager lock is held.
final ApkAssetsSupplier assetsSupplier = createApkAssetsSupplierNotLocked(key);
if (overrideDisplayId != null) {
rebaseKeyForDisplay(key, overrideDisplayId);
}
Resources resources;
if (activityToken != null) {
Configuration initialOverrideConfig = new Configuration(key.mOverrideConfiguration);
rebaseKeyForActivity(activityToken, key, overrideDisplayId != null);
resources = createResourcesForActivity(activityToken, key, initialOverrideConfig,
overrideDisplayId, classLoader, assetsSupplier);
} else {
resources = createResources(key, classLoader, assetsSupplier);
}
return resources;
}
- 这里的
activityToken代表是一个 Activity ResourcesKey封装了资源相关的关键变量,如resDir(apk资源的路径),overlayPaths, 后续资源的初始化需要用到ResourcesKey
因为 activityToken 一般情况下不为 null ,因此会执行后面的createResourcesForActivity
private Resources createResourcesForActivity(@NonNull IBinder activityToken,
@NonNull ResourcesKey key, @NonNull Configuration initialOverrideConfig,
@Nullable Integer overrideDisplayId, @NonNull ClassLoader classLoader,
@Nullable ApkAssetsSupplier apkSupplier) {
synchronized (mLock) {
// 创建 ResourcesImpl
ResourcesImpl resourcesImpl = findOrCreateResourcesImplForKeyLocked(key, apkSupplier);
//
return createResourcesForActivityLocked(activityToken, initialOverrideConfig,
overrideDisplayId, classLoader, resourcesImpl, key.mCompatInfo);
}
}
private @Nullable ResourcesImpl findOrCreateResourcesImplForKeyLocked(
@NonNull ResourcesKey key, @Nullable ApkAssetsSupplier apkSupplier) {
// 通过 ResourceKey 去缓存里面去找
ResourcesImpl impl = findResourcesImplForKeyLocked(key);
// 如果缓存里面没有,那么创建
if (impl == null || impl.getAppliedSharedLibsHash() != mSharedLibAssetsMap.size()) {
impl = createResourcesImpl(key, apkSupplier);
if (impl != null) {
// 将创建的 ResourcesImpl 添加到缓存里面
mResourceImpls.put(key, new WeakReference<>(impl));
}
}
return impl;
}
在 createResourcesForActivity 中
createResourcesImpl创建ResourcesImpl对象createResourcesForActivityLocked真正创建Resource对象
到这里为止,Resource 对象就会被创建,但是这里我们真正需要关注的是 ResourcesImpl,因为资源的加载,真正依赖的是 ResourcesImpl 对象。Resource 对象也会通过 resources.setImpl(impl) 持有 ResourcesImpl。 可以看一下 createResourcesForActivityLocked 这个方法
private Resources createResourcesForActivityLocked(@NonNull IBinder activityToken,
@NonNull Configuration initialOverrideConfig, @Nullable Integer overrideDisplayId,
@NonNull ClassLoader classLoader, @NonNull ResourcesImpl impl,
@NonNull CompatibilityInfo compatInfo) {
...
resources.setImpl(impl);
...
return resources;
}
我们接着继续分析 ResourcesImpl 这个对象是怎么创建的
private @Nullable ResourcesImpl createResourcesImpl(@NonNull ResourcesKey key,
@Nullable ApkAssetsSupplier apkSupplier) {
// 创建 AssetManager
final AssetManager assets = createAssetManager(key, apkSupplier);
final DisplayAdjustments daj = new DisplayAdjustments(key.mOverrideConfiguration);
daj.setCompatibilityInfo(key.mCompatInfo);
final Configuration config = generateConfig(key);
final DisplayMetrics displayMetrics = getDisplayMetrics(generateDisplayId(key), daj);
// 这里真正创建了 ResourcesImpl, 并且持有了 AssetManager
final ResourcesImpl impl = new ResourcesImpl(assets, displayMetrics, config, daj, true);
return impl;
}
createResourcesImpl 首先创建了 AssetManager 对象,最后创建了 ResourcesImpl,并持有这个 AssetManager,这里的 Configuration (特定的资源配置,如语言,分辨率)等其它的对象这里不过多分析,不是主要矛盾,跟资源加载相关的最重要的只有这个 AssetManager。 接下来分析 AssetManager 是如何创建的。
创建 AssetManager
protected @Nullable AssetManager createAssetManager(@NonNull final ResourcesKey key,
@Nullable ApkAssetsSupplier apkSupplier) {
final AssetManager.Builder builder = new AssetManager.Builder().setNoInit();
final ArrayList<ApkKey> apkKeys = extractApkKeys(key);
for (int i = 0, n = apkKeys.size(); i < n; i++) {
final ApkKey apkKey = apkKeys.get(i);
// 这里有两步,第一:通过 apkKey 去 load ApkAssets;第二,把 loaded ApkAssets 添加到 AssetManager 中
builder.addApkAssets( (apkSupplier != null) ? apkSupplier.load(apkKey) : loadApkAssets(apkKey));
}
if (key.mLoaders != null) {
for (final ResourcesLoader loader : key.mLoaders) {
builder.addLoader(loader);
}
}
return builder.build();
}
在 createAssetManager 中:
- 从前面提到的
ResourcesKey提取多个ApkKey - 通过 #1 提取到的
apkKeys加载ApkAssets - 通过
addApkAssets把ApkAssets添加到AssetManager中 - ApkAsset 加载完成之后, AssetManager 执行
build,根据“构建者”设计模式的一般套路,在 build 中肯定会 new AssetManager
我们接下来一步一步分析
Step1:提取 ApkKeys
private static @NonNull ArrayList<ApkKey> extractApkKeys(@NonNull final ResourcesKey key) {
final ArrayList<ApkKey> apkKeys = new ArrayList<>();
// apk 本身
if (key.mResDir != null) {
apkKeys.add(new ApkKey(key.mResDir, false /*sharedLib*/, false /*overlay*/));
}
if (key.mSplitResDirs != null) {
for (final String splitResDir : key.mSplitResDirs) {
apkKeys.add(new ApkKey(splitResDir, false /*sharedLib*/, false /*overlay*/));
}
}
// 提取 shared libs apk
if (key.mLibDirs != null) {
for (final String libDir : key.mLibDirs) {
// Avoid opening files we know do not have resources, like code-only .jar files.
if (libDir.endsWith(".apk")) {
apkKeys.add(new ApkKey(libDir, true /*sharedLib*/, false /*overlay*/));
}
}
}
// 提取 overlay apk
if (key.mOverlayPaths != null) {
for (final String idmapPath : key.mOverlayPaths) {
apkKeys.add(new ApkKey(idmapPath, false /*sharedLib*/, true /*overlay*/));
}
}
return apkKeys;
}
从 ResourceKey 中,提取多个资源包的路径,并传入 ApkKey,其中就包括本身进程所加载的 apk,shared lib apk 和 rro apk。接下来会根据这些 ApkKey 来加载对应的 ApkAssets
Step2:根据 ApkKey 加载 ApkAssets
public @NonNull ApkAssets loadApkAssets(@NonNull final ApkKey key) throws IOException {
ApkAssets apkAssets;
// 优先从缓存读取,代码省略
...
// 如果 apk 是一个 Overlay apk,那么从 /data/resource-cache/ 文件夹下读取对应的 @idmap 文件
// idmap 文件类似于: vendor@overlay@rro.apk@idmap
if (key.overlay) {
apkAssets = ApkAssets.loadOverlayFromPath(overlayPathToIdmapPath(key.path), flags);
} else {
// 如果是普通的 apk,那么就从保存 apk 的路径去读取 apkAsset
apkAssets = ApkAssets.loadFromPath(key.path, flags);
}
synchronized (mCachedApkAssets) {
mCachedApkAssets.put(key, new WeakReference<>(apkAssets));
}
return apkAssets;
}
// 对于 overlay apk,调用的是 ApkAssets.loadOverlayFromPath
public static @NonNull ApkAssets loadOverlayFromPath(@NonNull String idmapPath,
@PropertyFlags int flags) throws IOException {
return new ApkAssets(FORMAT_IDMAP, idmapPath, flags, null /* assets */);
}
// 对于普通的 apk, 调用的是 ApkAssets.loadFromPath
public static @NonNull ApkAssets loadFromPath(@NonNull String path, @PropertyFlags int flags) // 这里创建了一个 ApkAssets
return new ApkAssets(FORMAT_APK, path, flags, null /* assets */);
}
// 最终以上的两个方法都会调用相同的 ApkAssets 的构造函数如下
private ApkAssets(@FormatType int format, @NonNull String path, @PropertyFlags int flags, @Nullable AssetsProvider assets) throws IOException {
this(format, flags, assets);
mNativePtr = nativeLoad(format, path, flags, assets);
mStringBlock = new StringBlock(nativeGetStringBlock(mNativePtr), true /*useSparse*/);
}
在 loadApkAssets中会创建一个 ApkAssets java 对象,在 ApkAssets 构造函数中会执行 nativeLoad (从方法命名以及参数,可以猜测应该是通 native 方法加载 App 加载资源), 并创建 StringBlock 对象。
这里的 idmap 是一个映射文件,记录了 Target apk 和 Overlay apk 之间资源 ID 的映射关系。idmap 文件是在 PMS 安装的时候生成的,每个 overlay apk 都有一个 idmap 文件在 /data/resource-cache/ 目录下
创建 ApkAssets
我们先看 nativeLoad 的实现
nativeLoad 的实现位于 frameworks/base/core/jni/android_content_res_ApkAssets.cpp
static jlong NativeLoad(JNIEnv* env, jclass /*clazz*/, const format_type_t format,
jstring java_path, const jint property_flags, jobject assets_provider) {
auto loader_assets = LoaderAssetsProvider::Create(env, assets_provider);
AssetManager2::ApkAssetsPtr apk_assets;
switch (format) {
// 对于普通的 apk
case FORMAT_APK: {
auto assets = MultiAssetsProvider::Create(std::move(loader_assets),
ZipAssetsProvider::Create(path.c_str(),
property_flags));
apk_assets = ApkAssets::Load(std::move(assets), property_flags);
break;
}
// 对于 overlay apk
case FORMAT_IDMAP:
apk_assets = ApkAssets::LoadOverlay(path.c_str(), property_flags);
break;
...
}
...
return CreateGuardedApkAssets(std::move(apk_assets));
}
// 以普通的 apk 为例,我们接下来继续分析 ApkAssets::Load
// 在 ApkAssets::Load 中真正实现的是 ApkAssets::LoadImpl
// 在 ApkAssets::LoadImpl 中会首先 open 打包到 apk 里面的 "resources.arsc" 的文件
ApkAssetsPtr ApkAssets::LoadImpl(std::unique_ptr<AssetsProvider>&& assets,
package_property_t property_flags,
std::unique_ptr<Asset>&& idmap_asset,
std::unique_ptr<LoadedIdmap>&& loaded_idmap) {
// Open the resource table via mmap unless it is compressed. This logic is taken care of by Open.
bool resources_asset_exists = false;
// 打开 "resources.arsc" 文件
auto resources_asset = assets->Open(kResourcesArsc, Asset::AccessMode::ACCESS_BUFFER,
&resources_asset_exists);
...
return LoadImpl(std::move(resources_asset), std::move(assets), property_flags,
std::move(idmap_asset), std::move(loaded_idmap));
}
ApkAssetsPtr ApkAssets::LoadImpl(std::unique_ptr<Asset>&& resources_asset,
std::unique_ptr<AssetsProvider>&& assets,
package_property_t property_flags,
std::unique_ptr<Asset>&& idmap_asset,
std::unique_ptr<LoadedIdmap>&& loaded_idmap) {
std::unique_ptr<LoadedArsc> loaded_arsc;
// 加载正常 apk 的资源包
if (resources_asset != nullptr) {
const auto data = resources_asset->getIncFsBuffer(true /* aligned */);
const size_t length = resources_asset->getLength();
loaded_arsc = LoadedArsc::Load(data, length, loaded_idmap.get(), property_flags);
// loaded_idmap 不为 null,说明这个是 overlay 的资源包
} else if (loaded_idmap != nullptr && IsFabricatedOverlay(loaded_idmap->OverlayApkPath())) {
loaded_arsc = LoadedArsc::Load(loaded_idmap.get());
}
...
return ApkAssetsPtr::make(PrivateConstructorUtil{}, std::move(resources_asset),
std::move(loaded_arsc), std::move(assets), property_flags,
std::move(idmap_asset), std::move(loaded_idmap));
}
std::unique_ptr<LoadedArsc> LoadedArsc::Load(incfs::map_ptr<void> data,
const size_t length,
const LoadedIdmap* loaded_idmap,
const package_property_t property_flags) {
std::unique_ptr<LoadedArsc> loaded_arsc(new LoadedArsc());
ChunkIterator iter(data, length);
while (iter.HasNext()) {
const Chunk chunk = iter.Next();
switch (chunk.type()) {
case RES_TABLE_TYPE:
if (!loaded_arsc->LoadTable(chunk, loaded_idmap, property_flags)) {
return {};
}
...
}
}
...
return loaded_arsc;
}
bool LoadedArsc::LoadTable(const Chunk& chunk, const LoadedIdmap* loaded_idmap,
package_property_t property_flags) {
ChunkIterator iter(chunk.data_ptr(), chunk.data_size());
// resources.arsc 是通过一个个的 Chunk 组织成的,整体来说就是由 Head + String pool + N 个 Package 组成,其中 Package 又包括了多个子 Chunk
// 下面的代码就是遍历 resources.arsc 里面的 Chunk,然后进行解析
while (iter.HasNext()) {
const Chunk child_chunk = iter.Next();
switch (child_chunk.type()) {
// 解析 String pool Chunk
case RES_STRING_POOL_TYPE:
// Only use the first string pool. Ignore others.
if (global_string_pool_->getError() == NO_INIT) {
status_t err = global_string_pool_->setTo(child_chunk.header<ResStringPool_header>(),
child_chunk.size());
}
break;
// 解析 Package Chunk,由于 Package 相对比较复杂,Package Chunk 的解析单独由 LoadedPackage::Load 来解析
// Package Chunk 的解析流程不是我们的重点,如果以后碰到了可以再仔细看代码
case RES_TABLE_PACKAGE_TYPE: {
if (packages_seen + 1 > package_count) {
LOG(ERROR) << "More package chunks were found than the " << package_count
<< " declared in the header.";
return false;
}
packages_seen++;
std::unique_ptr<const LoadedPackage> loaded_package =
LoadedPackage::Load(child_chunk, property_flags);
if (!loaded_package) {
return false;
}
packages_.push_back(std::move(loaded_package));
} break;
}
if (iter.HadError()) {
LOG(ERROR) << iter.GetLastError();
if (iter.HadFatalError()) {
return false;
}
}
return true;
}
到这里 nativeload 的分析就结束了,我们总结一下,这部分代码可以大概分为三部分
- 加载 apk 里面的 resources.arsc 文件; 如果是 overlay apk,那么会加载 idmap 文件 + apk 本身的 resources.arsc 文件
- 对 resources.arsc 和 idmap 文件进行解析 (这里没有详细展开 resources.arsc 是如何一步步解析的)
- 返回一个包含 apk_assets 的指针给 Java 层的 mNativePtr
相关数据结构如下
ApkAssets 持有 LoadedArsc, LoaderArsc 持有多个 LoadedPackage
这里主要,这里出现了三个 string pool,global_string_pool, type_string_pool, 和 key_string_pool
type_string_pool
type_string_pool 存放的是该包内所有资源的类型名称,如 "layout", "drawable" 等。
key_string_pool
key_string_pool 存放的是该包内所有资源条目的名称,如 "app_name", "ic_launcher" 等。
global_string_pool
global_string_pool 存储的是资源的实际内容值,是资源在运行时要展示或使用的文字和数据。
举个简单的例子,比如在 xml 文件中定义
<string name="app_name">Target App</string>
其中, string 存在 type_string_pool 中, app_name 存储在 key_string_pool 中, Target App 存储在 global_string_pool 中
接下来继续分析 new StringBlock 的行为
private ApkAssets(@FormatType int format, @NonNull String path, @PropertyFlags int flags, @Nullable AssetsProvider assets) throws IOException {
...
mStringBlock = new StringBlock(nativeGetStringBlock(mNativePtr), true /*useSparse*/);
}
// nativeGetStringBlock 对应的 native 方法为
static jlong NativeGetStringBlock(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr) {
auto scoped_apk_assets = ScopedLock(ApkAssetsFromLong(ptr));
auto apk_assets = scoped_apk_assets->get();
return reinterpret_cast<jlong>(apk_assets->GetLoadedArsc()->GetStringPool());
}
这里就比较简单了,在上一步返回了 apk_assets,这里就通过 apk_assets 的 loaded_arsc 拿到 global_string_pool。
到这里为止,ApkAssets 的加载就已经完成了,主要工作就是记载并解析 Apk 里的 resources.arsc,并返回 java 层由 ApkAsset.mNativePtr 持有。
Step3 : Add ApkAssets
Apk 的 ApkAsset 加载完成之后,接下来 AssetManager 会 addApkAssets
// 首先调用 addApkAssets
public Builder addApkAssets(ApkAssets apkAssets) {
mUserApkAssets.add(apkAssets);
return this;
}
addApkAssets 会把多个 ApkAsset 保存到 AssetManager 中并由 AssetManager.mUserApkAssets 这个数组持有
Step4 : AssetManager build()
public AssetManager build() {
// 加载 Framework 的资源,如 "/system/framework/framework-res.apk"
final ApkAssets[] systemApkAssets = getSystem().getApkAssets();
// 如果多个加载器提供了相同的资源,这些资源只会被包含一次,避免重复;最后一个出现的资源将决定其在 `AssetManager` 中的加载顺序
final ArrayList<ApkAssets> loaderApkAssets = new ArrayList<>();
final ArraySet<ApkAssets> uniqueLoaderApkAssets = new ArraySet<>();
for (int i = mLoaders.size() - 1; i >= 0; i--) {
final List<ApkAssets> currentLoaderApkAssets = mLoaders.get(i).getApkAssets();
for (int j = currentLoaderApkAssets.size() - 1; j >= 0; j--) {
final ApkAssets apkAssets = currentLoaderApkAssets.get(j);
if (uniqueLoaderApkAssets.add(apkAssets)) {
loaderApkAssets.add(0, apkAssets);
}
}
}
final int totalApkAssetCount = systemApkAssets.length + mUserApkAssets.size()
+ loaderApkAssets.size();
final ApkAssets[] apkAssets = new ApkAssets[totalApkAssetCount];
System.arraycopy(systemApkAssets, 0, apkAssets, 0, systemApkAssets.length);
// Append user ApkAssets after system ApkAssets.
for (int i = 0, n = mUserApkAssets.size(); i < n; i++) {
apkAssets[i + systemApkAssets.length] = mUserApkAssets.get(i);
}
// Append ApkAssets provided by loaders to the end.
for (int i = 0, n = loaderApkAssets.size(); i < n; i++) {
apkAssets[i + systemApkAssets.length + mUserApkAssets.size()] =
loaderApkAssets.get(i);
}
// 真正创建 AssetManager 对象,并且会创建一个 native 对象 AssetManager2
final AssetManager assetManager = new AssetManager(false /*sentinel*/);
assetManager.mApkAssets = apkAssets;
// assetManager.mObject 就是上一步创建的 native 对象 AssetManager2
AssetManager.nativeSetApkAssets(assetManager.mObject, apkAssets,
false /*invalidateCaches*/, mNoInit /*preset*/);
assetManager.mLoaders = mLoaders.isEmpty() ? null
: mLoaders.toArray(new ResourcesLoader[0]);
return assetManager;
}
- 在
build中创建了AssetManager对象,并且创建了 Native 对象AssetManager2 - 通过
nativeSetApkAssetsset 所有的 ApkAsset,包括了
- Apk 本身的 ApkAssets
- shared libs 的 ApkAssets (如果存在)
- framework-res.apk 的 ApkAssets
- RRO apk 的 ApkAssets (如果存在)
接下来我们看看 AssetManager 的构造函数里面做了什么
创建 Native 对象 AssetManager2
private AssetManager(boolean sentinel) {
创建了一个 native 对象
mObject = nativeCreate();
...
}
// android_util_AssetManager.cpp
static jlong NativeCreate(JNIEnv* /*env*/, jclass /*clazz*/) {
// 创建了 AssetManager2 对象,所谓的 GuardedAssetManager,其实就是对 AssetManager2 访问加锁
return reinterpret_cast<jlong>(new GuardedAssetManager());
}
在 AssetManager 的构造函数中,就会生成 AssetManager2 这个 native 对象,并且由 Java 层的AssetManager 的 mObject 持有,并且作为 nativeSetApkAssets 中参数。接下来看 AssetManager.nativeSetApkAssets 如何设置 ApkAssets
Native Set ApkAssets
// java 层调用 nativeSetApkAssets
AssetManager.nativeSetApkAssets(assetManager.mObject, apkAssets,
false /*invalidateCaches*/, mNoInit /*preset*/);
// native 层对应的代码为
static void NativeSetApkAssets(JNIEnv* env, jclass /*clazz*/, jlong ptr,
jobjectArray apk_assets_array, jboolean invalidate_caches,
jboolean preset) {
const jsize apk_assets_len = env->GetArrayLength(apk_assets_array);
std::vector<AssetManager2::ApkAssetsPtr> apk_assets;
apk_assets.reserve(apk_assets_len);
for (jsize i = 0; i < apk_assets_len; i++) {
jobject obj = env->GetObjectArrayElement(apk_assets_array, i);
...
auto scoped_assets = ScopedLock(ApkAssetsFromLong(apk_assets_native_ptr));
apk_assets.emplace_back(*scoped_assets);
}
// 通过 Java 层传过来的 AssetManager2 的指针,获取对应的 AssetManager2 对象
auto assetmanager = LockAndStartAssetManager(ptr);
...
assetmanager->SetApkAssets(apk_assets, invalidate_caches);
}
接下来看 AssetManager2 如何设置这些 ApkAssets,但是在分析如何设置 ApkAssets 之前,写介绍重要且相关的数据结构,方便后续的分析
class AssetManager2 {
// These are ordered according to apk_assets_. The mappings may change depending on what is
// in apk_assets_, therefore they must be stored in the AssetManager and not in the
// immutable ApkAssets class.
std::vector<PackageGroup> package_groups_;
// The current configurations set for this AssetManager. When this changes, cached resources
// may need to be purged.
ftl::SmallVector<ResTable_config, 1> configurations_;
// An array mapping package ID to index into package_groups. This keeps the lookup fast
// without taking too much memory.
std::array<uint8_t, std::numeric_limits<uint8_t>::max() + 1> package_ids_ = {};
struct PackageGroup {
// ConfiguredPackage 内部持有 LoadedPackage 和 配置相关的信息
std::vector<ConfiguredPackage> packages_;
// ApkAssetsCookie 标识 package 所在的 ApkAssets 的对象的索引, 所以指向的是一个 ApkAssets
std::vector<ApkAssetsCookie> cookies_;
// Runtime Resource Overlays that overlay resources in this package group.
std::vector<ConfiguredOverlay> overlays_;
// 保存资源共享库编译时 id 和运行时 id 的一个映射关系
std::shared_ptr<DynamicRefTable> dynamic_ref_table = std::make_shared<DynamicRefTable>();
};
struct ConfiguredPackage {
// A pointer to the immutable, loaded package info.
const LoadedPackage* loaded_package_;
// A mutable AssetManager-specific list of configurations that match the AssetManager's
// current configuration. This is used as an optimization to avoid checking every single
// candidate configuration when looking up resources.
ByteBucketArray<FilteredConfigGroup> filtered_configs_;
};
}
LoadedPackage
LoadedPackage 在上面描述 ApkAsset 已经描述过,它承载了 Apk 一整套资源的命名和索引信息。一个 Apk 的 resources.arsc 通常情况下会包含 1 个 LoadedPackage,但是 Apk 在编译时候链接了多个其他的资源库,那么就会存在多个 LoadedPackage。
PackageGroup
PackageGroup包含了多个来自于不同 Apk 的LoadedPackage, 它把 target Apk 和 Overlay Apk 的资源都整合到同一个PackageGroup中cookies_描述对应的 LoadedPackage 是来自于哪一个 Apkoverlays_描述了针对这个 target apk 的所有 Overlay Apk 的资源
package_groups_
由一组 PackageGroup 组成
package_ids_
存储 LoadedPackage id 和 package_groups_ 中 PackageGroup 的映射关系,所以通过 LoadedPackage id 能够找到与之一一对应的 PackageGroup
configurations_
当前 AssetManger2 的配置信息,如屏幕分辨率,语言等。如果发生改变,需要丢弃缓存的资源
了解了基本的数据结构之后,就可以分析 SetApkAssets 的具体过程,
AssetManager2.SetApkAssets
bool AssetManager2::SetApkAssets(ApkAssetsList apk_assets, bool invalidate_caches) {
BuildDynamicRefTable(apk_assets);
...
return true;
}
SetApkAssets 的主要实现是 BuildDynamicRefTable
BuildDynamicRefTable 过程比较复杂,拆解分析
void AssetManager2::BuildDynamicRefTable(ApkAssetsList apk_assets) {
apk_assets_.resize(apk_assets.size());
for (size_t i = 0; i != apk_assets.size(); ++i) {
apk_assets_[i].first = apk_assets[i];
// Let's populate the locked assets right away as we're going to need them here later.
apk_assets_[i].second = apk_assets[i];
}
// package_groups_ 是 PackageGroup 的集合
package_groups_.clear();
// package_ids_ 的 key: ApkAssets 的 id
// value: PackageGroup 在 package_groups_ 的索引
package_ids_.fill(0xff);
std::unordered_map<std::string_view, uint8_t> target_assets_package_ids;
std::vector<const ApkAssets*> sorted_apk_assets;
sorted_apk_assets.reserve(apk_assets.size());
for (auto& asset : apk_assets) {
sorted_apk_assets.push_back(asset.get());
}
// sorted_apk_assets 是对输入的 apk_assets 的排序,把 rro 的 ApkAssets 放在最后
std::stable_partition(sorted_apk_assets.begin(), sorted_apk_assets.end(),
[](auto a) { return !a->IsOverlay(); });
std::unordered_map<const ApkAssets*, ApkAssetsCookie> apk_assets_cookies;
apk_assets_cookies.reserve(apk_assets.size());
for (size_t i = 0, n = apk_assets.size(); i < n; i++) {
apk_assets_cookies[apk_assets[i].get()] = static_cast<ApkAssetsCookie>(i);
}
// 0x01 is reserved for the android package.
int next_package_id = 0x02;
...
}
- 把从 java 层传过来的 apk_assets 保存到 apk_assets_ 中
- 对 apk_assets_ 中的 ApkAssets 进行排序,把 RRO 的 ApkAssets 排在后面 (排序对于 RRO 机制非常重要,在后面提到的资源加载中这个排序决定了资源最终来自于哪一个 ApkAssets)
// 遍历每一个 ApkAssets
for (const ApkAssets* apk_assets : sorted_apk_assets) {
std::shared_ptr<OverlayDynamicRefTable> overlay_ref_table;
// 处理 rro apk 的 ApkAssets
if (auto loaded_idmap = apk_assets->GetLoadedIdmap(); loaded_idmap != nullptr) {
// 找到对应的 target apk
auto iter = target_assets_package_ids.find(loaded_idmap->TargetApkPath());
if (iter == target_assets_package_ids.end()) {
LOG(INFO) << "failed to find target package for overlay "
<< loaded_idmap->OverlayApkPath();
} else {
uint8_t target_package_id = iter->second;
// 处理 Overlay 资源
overlay_ref_table = std::make_shared<OverlayDynamicRefTable>(
loaded_idmap->GetOverlayDynamicRefTable(target_package_id));
// 将 Overlay 资源添加到 target apk 的 Overlay 列表 target_package_group.overlays_ 中
const uint8_t target_idx = package_ids_[target_package_id];
PackageGroup& target_package_group = package_groups_[target_idx];
// 把 Overlay 资源添加到 target 的 PackageGroup 的 overlays_ 中
// 这样 Overlay 资源和 target 资源放在了同一个 PackageGroup 中
target_package_group.overlays_.push_back(
ConfiguredOverlay{loaded_idmap->GetTargetResourcesMap(target_package_id,
overlay_ref_table.get()),
apk_assets_cookies[apk_assets]});
}
}
// 处理 普通 apk 的 ApkAssets
// 获取 ApkAssets 的 LoadedArsc 对象,
// ApkAssets LoadedArsc 和 LoadedPackage 之间的关系在上面以及提到过,不清楚的可以本节上面关于 ApkAssets 的内容
const LoadedArsc* loaded_arsc = apk_assets->GetLoadedArsc();
for (const std::unique_ptr<const LoadedPackage>& package : loaded_arsc->GetPackages()) {
// Get the package ID or assign one if a shared library.
int package_id;
if (package->IsDynamic()) {
package_id = next_package_id++;
} else {
package_id = package->GetPackageId();
}
uint8_t idx = package_ids_[package_id];
if (idx == 0xff) {
// Add the mapping for package ID to index if not present.
package_ids_[package_id] = idx = static_cast<uint8_t>(package_groups_.size());
// 新建一个 PackageGroup
PackageGroup& new_group = package_groups_.emplace_back();
if (overlay_ref_table != nullptr) {
// If this package is from an overlay, use a dynamic reference table that can rewrite
// overlay resource ids to their corresponding target resource ids.
new_group.dynamic_ref_table = std::move(overlay_ref_table);
}
DynamicRefTable* ref_table = new_group.dynamic_ref_table.get();
ref_table->mAssignedPackageId = package_id;
ref_table->mAppAsLib = package->IsDynamic() && package->GetPackageId() == 0x7f;
}
// Add the package to the set of packages with the same ID.
PackageGroup* package_group = &package_groups_[idx];
package_group->packages_.emplace_back().loaded_package_ = package.get();
package_group->cookies_.push_back(apk_assets_cookies[apk_assets]);
}
// Add the package name -> build time ID mappings.
for (const DynamicPackageEntry& entry : package->GetDynamicPackageMap()) {
String16 package_name(entry.package_name.c_str(), entry.package_name.size());
package_group->dynamic_ref_table->mEntries.replaceValueFor(
package_name, static_cast<uint8_t>(entry.package_id));
}
if (auto apk_assets_path = apk_assets->GetPath()) {
// Overlay target ApkAssets must have been created using path based load apis.
target_assets_package_ids.emplace(*apk_assets_path, package_id);
}
}
...
}
遍历每一个 ApkAssets, 对于普通 Apk 的 ApkAssets
- 为每一个 ApkAssets 创建一个新的 PackageGroup
- 填充 PackageGroup 的 packages_ 和 cookies_
- 把 PackageGroup 添加到 package_groups_ 中,并且建立 package_id 和 package_groups_ 的映射关系,方便后续通过 package_id 找到对应的 PackageGroup
对于 RRO Apk 的 ApkAssets
- 在
target_assets_package_ids中找到需要覆盖的 Apk,并且在 package_groups_ 中找到对应的 PackageGroup - 填充 PackageGroup 的 overlays_, 把 Target Apk的资源和 RRO Apk 的资源绑定在一个 PackageGroup 中
// Now assign the runtime IDs so that we have a build-time to runtime ID map.
DynamicRefTable::AliasMap aliases;
for (const auto& group : package_groups_) {
const std::string& package_name = group.packages_[0].loaded_package_->GetPackageName();
const auto name_16 = String16(package_name.c_str(), package_name.size());
for (auto&& inner_group : package_groups_) {
inner_group.dynamic_ref_table->addMapping(name_16,
group.dynamic_ref_table->mAssignedPackageId);
}
for (const auto& package : group.packages_) {
const auto& package_aliases = package.loaded_package_->GetAliasResourceIdMap();
aliases.insert(aliases.end(), package_aliases.begin(), package_aliases.end());
}
}
if (!aliases.empty()) {
std::sort(aliases.begin(), aliases.end(), [](auto&& l, auto&& r) { return l.first < r.first; });
// Add the alias resources to the dynamic reference table of every package group. Since
// staging aliases can only be defined by the framework package (which is not a shared
// library), the compile-time package id of the framework is the same across all packages
// that compile against the framework.
for (auto& group : std::span(package_groups_.data(), package_groups_.size() - 1)) {
group.dynamic_ref_table->setAliases(aliases);
}
package_groups_.back().dynamic_ref_table->setAliases(std::move(aliases));
}
最后就是收尾阶段了,把 build-time 的资源 ID 和 runtime 的资源 ID 建立映射关系,比如你的 App 在 build-time 的时候引用了一个 shard library 的资源,比如 com.google.android.material, 在编译的时候这个资源 ID 可能是 0x7f01xxxx, 但是在运行时,这个 shared library 被系统分配的 ID 是 0x0201xxxx,所以必须要建立 build-time 和 runtime ID 之间的映射。
到这里 BuildDynamicRefTable就分析完了
BuildDynamicRefTable 主要的作用就是把多个 ApkAssets 的资源解析到 PackageGroup package_groups_ 和 package_ids_ 这些变量中,后续资源的加载的过程就可以依赖于这些变量匹配到对应的资源。
资源初始化总结
Android 系统对于资源的初始化,这个过程主要的工作就是加载并解析多个 ApkAssets,建立 Target apk 和 Overlay apk 之间的映射关系,最终由 AssetManager 来管理这些资源方便后续的资源加载。