接着第一篇文章,我们继续分析插件加载的方法:Plugin.load
final boolean load(int load, boolean useCache) {
PluginInfo info = mInfo;
//继续去看loadLocked这个方法
boolean rc = loadLocked(load, useCache);
// 尝试在此处调用Application.onCreate方法
// Added by Jiongxuan Zhang
if (load == LOAD_APP && rc) {
callApp();
}
return rc;
}
//只加载Service/Activity/ProviderInfo信息(包含ComponentList)
static final int LOAD_INFO = 0;
//加载插件信息和资源
static final int LOAD_RESOURCES = 1;
//加载插件信息、资源和Dex
static final int LOAD_DEX = 2;
//加载插件信息、资源、Dex,并运行Entry类
static final int LOAD_APP = 3;
private boolean loadLocked(int load, boolean useCache) {
// 这里只看新建的情况即useCache = false且load = LOAD_APP的情况,省略缓存的情况。
// 若插件被“禁用”,则即便上次加载过(且进程一直活着),这次也不能再次使用了
int status = PluginStatusController.getStatus(mInfo.getName(), mInfo.getVersion());
if (status < PluginStatusController.STATUS_OK) {
return false;
}
Context context = mContext;
ClassLoader parent = mParent;
PluginCommImpl manager = mPluginManager;
ProcessLocker lock = new ProcessLocker(context, lockFileName);
if (!lock.tryLockTimeWait(5000, 10)) {
// 此处仅仅打印错误
if (LOGR) {
LogRelease.w(PLUGIN_TAG, logTag + ": failed to lock: can't wait plugin ready");
}
}
boolean rc = doLoad(logTag, context, parent, manager, load);
lock.unlock();
if (rc) {
try {
// 至此,该插件已开始运行
PluginManagerProxy.addToRunningPluginsNoThrows(mInfo.getName());
} catch (Throwable e) {
if (LOGR) {
LogRelease.e(PLUGIN_TAG, "p.u.1: " + e.getMessage(), e);
}
}
return true;
}
// 删除一些文件,重试上面的流程。。。
}
private final boolean doLoad(String tag, Context context, ClassLoader parent, PluginCommImpl manager, int load) {
if (mLoader == null) {
// 试图释放文件
// 根据PluginInfo提供的路径提取文件到指定插件目录,其中so库的提取要根据Abi来获取对应的文件
//这里释放文件可以类比PMS安装文件的过程,加上下面loadDex方法的loadInfo过程
//参考 https://blog.csdn.net/Innost/article/details/47253179 中4.4节APK Installation分析
PluginInfo info = null;
if (mInfo.getType() == PluginInfo.TYPE_BUILTIN) {
File dir = context.getDir(Constant.LOCAL_PLUGIN_SUB_DIR, 0);
File dexdir = mInfo.getDexParentDir();
String dstName = mInfo.getApkFile().getName();
boolean rc = AssetsUtils.quickExtractTo(context, mInfo, dir.getAbsolutePath(), dstName,
dexdir.getAbsolutePath());
if (!rc) {
return false;
}
File file = new File(dir, dstName);
info = (PluginInfo) mInfo.clone();
info.setPath(file.getPath());
// FIXME 不应该是P-N,即便目录相同,未来会优化这里
info.setType(PluginInfo.TYPE_PN_INSTALLED);
} else if (mInfo.getType() == PluginInfo.TYPE_PN_JAR) {
// 外置插件,多了对插件的校验,通过文件头的一些字段,校验长度是否一致
// 插件版本,MD5校验,证书校验
} else {
//
}
mLoader = new Loader(context, mInfo.getName(), mInfo.getPath(), this);
if (!mLoader.loadDex(parent, load)) {
return false;
}
// 设置插件为“使用过的”
// 注意,需要重新获取当前的PluginInfo对象,而非使用“可能是新插件”的mInfo
PluginManagerProxy.updateUsedIfNeeded(mInfo.getName(), true);
// 若需要加载Dex,则还同时需要初始化插件里的Entry对象
if (load == LOAD_APP) {
// NOTE Entry对象是可以在任何线程中被调用到
//调用插件Library的entry方法,传递在loadDex创建好的PluginContext给插件
// 并初始化插件的环境(初始化一些反射的方法,然后调用Factory2的方法)。
// 并且通过将插件中的Activity通过gradle 在编译期改成继承了PluginActivity形成了一个插件环境。
//https://mp.weixin.qq.com/s/Vuh6SxFnTgiUsdX-ofeb4g
// PluginActivity在获取context改成通过Factory2方法调用获取插件的context(PluginContext)
// 重写启动组件的方法改成Replugin提供的启动方法(比如启动Activity通过把目标Activity改成
//mainfest中的坑位达到欺骗系统的效果)
if (!loadEntryLocked(manager)) {
return false;
}
}
}
}
解析四大组件,并保存到对应的数据结构,后面启动插件中的组件时使用
final boolean loadDex(ClassLoader parent, int load) {
try {
PackageManager pm = mContext.getPackageManager();
mPackageInfo = Plugin.queryCachedPackageInfo(mPath);
if (mPackageInfo == null) {
// PackageInfo 根据apk里面的mainfest解析xml获取四大组件的信息
mPackageInfo = pm.getPackageArchiveInfo(mPath,
PackageManager.GET_ACTIVITIES | PackageManager.GET_SERVICES |
PackageManager.GET_PROVIDERS | PackageManager.GET_RECEIVERS |
PackageManager.GET_META_DATA);
mPackageInfo.applicationInfo.sourceDir = mPath;
mPackageInfo.applicationInfo.publicSourceDir = mPath;
if (TextUtils.isEmpty(mPackageInfo.applicationInfo.processName)) {
mPackageInfo.applicationInfo.processName = mPackageInfo.applicationInfo.packageName;
}
// 添加针对SO库的加载
// 此属性最终用于ApplicationLoaders.getClassLoader,在创建PathClassLoader时成为其参数
// 这样findLibrary可不用覆写,即可直接实现SO的加载
// Added by Jiongxuan Zhang
PluginInfo pi = mPluginObj.mInfo;
File ld = pi.getNativeLibsDir();
mPackageInfo.applicationInfo.nativeLibraryDir = ld.getAbsolutePath();
}
// 创建或获取ComponentList表
// Added by Jiongxuan Zhang
mComponents = Plugin.queryCachedComponentList(mPath);
if (mComponents == null) {
// ComponentList
mComponents = new ComponentList(mPackageInfo, mPath, mPluginObj.mInfo);
// 动态注册插件中声明的 receiver
regReceivers();
/* 只调整一次 */
// 调整插件中组件的进程名称
adjustPluginProcess(mPackageInfo.applicationInfo);
// 调整插件中 Activity 的 TaskAffinity
adjustPluginTaskAffinity(mPluginName, mPackageInfo.applicationInfo);
}
if (load == Plugin.LOAD_INFO) {
return isPackageInfoLoaded();
}
mPkgResources = Plugin.queryCachedResources(mPath);
// LOAD_RESOURCES和LOAD_ALL都会获取资源,但LOAD_INFO不可以(只允许获取PackageInfo)
if (mPkgResources == null) {
//调用ActivityThread.getTopLevelResources 获取资源,AssetManager通过路径去加载
// resource.asrc获取资源的索引信息。之后就可以通过资源索引来加载资源了
mPkgResources = pm.getResourcesForApplication(mPackageInfo.applicationInfo);
// 缓存表: Resources
synchronized (Plugin.FILENAME_2_RESOURCES) {
Plugin.FILENAME_2_RESOURCES.put(mPath, new WeakReference<>(mPkgResources));
}
}
if (load == Plugin.LOAD_RESOURCES) {
return isResourcesLoaded();
}
mClassLoader = Plugin.queryCachedClassLoader(mPath);
if (mClassLoader == null) {
//获取Dex(优化后)生成时所在的目录
String out = mPluginObj.mInfo.getDexParentDir().getPath();
parent = getClass().getClassLoader().getParent(); // TODO: 这里直接用父类加载器
String soDir = mPackageInfo.applicationInfo.nativeLibraryDir;
long begin = 0;
boolean isDexExist = false;
//创建PluginDexClassLoader,持有宿主ClassLoader,可以使用只有宿主才有的类
//ClassLoader借助DexPathList对传入路径(dex路径、so路径)解析
//然后在ClassLoader的findClass中通过DexPathList将dex文件加载进内存
//详细过程可以看 https://www.cnblogs.com/lanrenxinxin/p/4712224.html
mClassLoader = RePlugin.getConfig().getCallbacks().createPluginClassLoader(mPluginObj.mInfo,
mPath, out, soDir, parent);
}
if (load == Plugin.LOAD_DEX) {
return isDexLoaded();
}
// Context
mPkgContext = new PluginContext(mContext, android.R.style.Theme, mClassLoader, mPkgResources,
mPluginName, this);
} catch (Throwable e) {
return false;
}
return true;
}
参考:
Gradle插件在RePlugin中的应用:https://mp.weixin.qq.com/s/Vuh6SxFnTgiUsdX-ofeb4g
浅析dex文件加载机制:https://www.cnblogs.com/lanrenxinxin/p/4712224.html
[深入理解Android卷二 全文-第四章]深入理解:PackageManagerServicehttps://blog.csdn.net/Innost/article/details/47253179
网友评论