上一篇我们已经完成了一个真正可运行的插件化demo,而且demo中也解决了插件中不可以使用资源的问题,但是由于篇幅的问题我们并没有对原理讲解,所以这一篇是对上一篇的一个收尾,如果没有看过上一篇建议先看Android插件化完美实现代码资源加载及原理讲解 附可运行demo.
demo地址 : https://github.com/ljqloveyou123/LiujiaqiAndroid
我们的宿主应用调用一个未安装的插件apk,正常的情况下是不能访问插件中的资源的,例如R.,因为我们宿主中根本就不存在这个资源id,所以就会崩溃。还有另一种情况,基于我们上一篇的demo中,我们使用了占坑的方式加载了插件中apk中的Activity,上一篇我们也分析了创建Activity的时候需要Classloader,这个Classloader是通过 r.packageInfo.getClassloader()来获取的,而 r.packageInfo是一个LoadedApk类型的对象,这个对象是一个apk在内存中的标示, Apk文件的相关信息,诸如Apk文件的代码和资源,甚至代码里面的Activity,Service等四大组件的信息我们都可以通过此对象获取。我们再看一下这个LoadedApk对象怎么创建
private LoadedApk getPackageInfo(ApplicationInfo aInfo, CompatibilityInfo compatInfo,
ClassLoader baseLoader, boolean securityViolation, boolean includeCode,
boolean registerPackage) {
synchronized (mResourcesManager) {
WeakReference<LoadedApk> ref;
if (includeCode) {
ref = mPackages.get(aInfo.packageName);
} else {
ref = mResourcePackages.get(aInfo.packageName);
}
LoadedApk packageInfo = ref != null ? ref.get() : null;
if (packageInfo == null || (packageInfo.mResources != null
&& !packageInfo.mResources.getAssets().isUpToDate())) {
if (localLOGV) Slog.v(TAG, (includeCode ? "Loading code package "
: "Loading resource-only package ") + aInfo.packageName
+ " (in " + (mBoundApplication != null
? mBoundApplication.processName : null)
+ ")");
//这里创建,看一下他的参数
packageInfo
new LoadedApk(this, aInfo, compatInfo, baseLoader,
securityViolation, includeCode &&
(aInfo.flags&ApplicationInfo.FLAG_HAS_CODE) != 0, registerPackage);
。。。
}
return packageInfo;
}
}
我们看一下构造参数的第二个参数,aInfo,这是一个ApplicationInfo类型的对象,我们上一篇并没有构造我们自己的LoadedApk对象,而只是将我们的dex和宿主的合并了而已,那么也就是说了,这个LoadedApk里传入的ApplicationInfo其实是我们宿主的,并不是插件apk中的。那么也就是说如果我们在插件apk中直接使用资源,等到插件apk被宿主调用器后,使用的是宿主的资源库,而宿主的资源中并没有我们插件apk中的资源,所有一运行的时候就会报错。那么要解决这个问题我们就得想办法,让插件apk运行的时候使用自己的资源才行,下面我们分析。
我们在代码中使用资源的时候都是通过R.,或者是Context.g