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);
if (DEBUG) {
Slog.d(TAG, “- creating impl=” + impl + " with key: " + key);
}
return impl;
}
AssetManager是在这里创建的!它的功能不用多说了吧,可以加载任意路径下的资源,那么我们就自己创建一个AssetManager来替换掉原来的,怎么创建呢,继续看它怎么创建的
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 {
// 添加 ApkAssets 对象,加载apk资源
builder.addApkAssets(loadApkAssets(key.mResDir, false /sharedLib/,
false /overlay/));
} catch (IOException e) {
Log.e(TAG, "failed to add asset path " + key.mResDir);
return null;
}
}
// 。。。
return builder.build();
}
可以看到,AssetManager在创建的时候,添加了一个ApkAssets对象,这里好像无从下手。怎么办?其实看过API26的同学都知道,AssetManager中有一个addAssetPath方法,在API26以前,都是通过这个方法创建的,虽然这个方法 现在被标记为过时了,但还是可以反射到的。
插件资源加载
下面我们就来撸码,替换掉插件的资源加载器:
fun loadAsset(context: Context): Resources? {
try {
// 创建AssetManager对象
val assetManager = AssetManager::class.java.newInstance()
// 执行addAssetPath方法,添加资源加载路径
val addAssetPathMethod =
assetManager::class.java.getDeclaredMethod(“addAssetPath”, String::class.java)
addAssetPathMethod.isAccessible = true
addAssetPathMethod.invoke(assetManager, “sdcard/plugin-debug.apk”)
// 创建Resources
return Resources(
assetManager,
context.resources.displayMetrics,
context.resources.configuration
)
} catch (e: Exception) {
e.printStackTrace()
}
return null
}
在这里我们创建一个插件的资源加载器,然后在应用启动的时候替换掉,在Application中:
class DynamicApp : Application() {
// 插件的资源
private var mResources: Resources? = null
override fun onCreate() {
super.onCreate()
// 获取插件的资源
mResources = LoadUtils.loadAsset(this)
}
// 在这里重写getResources方法进行替换
override fun getResources(): Resources {
if (mResources == null) {
return super.getResources()
}
return mResources!!
}
}
在宿主里面替换完成,插件里面要使用宿主的Resources怎么办?很简单,直接使用application的Resources不就可以了吗。因为插件中的类是会合并到宿主的,所以他们的Application是相同的。
所以可以在插件里面创建一个BaseActivity, 重写getResources方法,让它使用application中的:
abstract class BaseActivity : Activity() {
override fun getResources(): Resources {
if (application != null && application.resou