前言
在初始化流程结束之后,本章节主要是继续按执行顺序,分析加载流程。
目录
前言
加载过程
1. 预加载 (SplitLoadManagerImpl)
1.1 SplitLoadManagerImpl.loadInstalledSplitsInternal
1.2 SplitLoadManagerImpl.createInstalledSplitFileIntents
1.3 SplitLoadManagerImpl.createLastInstalledSplitFileIntent
1.4 预加载过程总结
2. 加载 (SplitLoadManagerImpl)
2.1 SplitLoadManagerImpl.createSplitLoadTask
2.2 SplitLoadTask
2.3 SplitLoadHandler.loadSplits()
2.4 加载过程总结
加载过程
加载过程依靠SplitLoadManager插件加载管理器实现,SplitLoadManagerService维护了实现加载功能的SplitLoadManager实例,其真正实现类为SplitLoadManagerImpl。
1. 预加载 (SplitLoadManagerImpl)
上一节在Qigsaw.onApplicationCreated()的最后,
调用了Qigsaw.preloadInstalledSplits(Arrays.asList(QigsawConfig.DYNAMIC_FEATURES));
↓
SplitLoadManagerService.getInstance().preloadInstalledSplits(splitNames),这里传入的splitNames就是QigsawConfig里配置的Split工程名。
调用了Split加载服务管理类的预加载方法,然后看到SplitLoadManagerImpl的preloadInstalledSplits方法:
@Override
public void preloadInstalledSplits(Collection splitNames) {
if (!qigsawMode) {
return;
}
if (isProcessAllowedToWork()) {
loadInstalledSplitsInternal(splitNames);
}
}
判断了是否是Qigsaw工程,以及包名验证,然后调用loadInstalledSplitsInternal:
1.1 SplitLoadManagerImpl.loadInstalledSplitsInternal
private void loadInstalledSplitsInternal(Collection splitNames) {
SplitInfoManager manager = SplitInfoManagerService.getInstance();
if (manager == null) {
SplitLog.w(TAG, "Failed to get SplitInfoManager instance, have you invoke Qigsaw#install(...) method?");
return;
}
Collection splitInfoList;
if (splitNames == null) {
splitInfoList = manager.getAllSplitInfo(getContext());
} else {
splitInfoList = manager.getSplitInfos(getContext(), splitNames);
}
if (splitInfoList == null || splitInfoList.isEmpty()) {
SplitLog.w(TAG, "Failed to get Split-Info list!");
return;
}
//main process start to uninstall splits, other processes don't load pending uninstall splits.
List splitFileIntents = createInstalledSplitFileIntents(splitInfoList);
if (splitFileIntents.isEmpty()) {
SplitLog.w(TAG, "There are no installed splits!");
return;
}
createSplitLoadTask(splitFileIntents, null).run();
}
首先通过SplitInfoManagerService的实现类获取插件信息SplitInfo的集合splitInfoList.
(SplitInfoManagerService 获取插件信息主要是通过读取qigsaw/qigsaw_1.0.0_1.0.0.json中的内容获取插件信息。)
然后 通过createInstalledSplitFileIntents创建需要执行加载的任务Intent集合:splitFileIntents
1.2 SplitLoadManagerImpl.createInstalledSplitFileIntents
private List createInstalledSplitFileIntents(@NonNull Collection splitInfoList) {
List splitFileIntents = new ArrayList<>();
for (SplitInfo splitInfo : splitInfoList) {
if (canBeWorkedInThisProcessForSplit(splitInfo)) {
if (getLoadedSplitNames().contains(splitInfo.getSplitName())) {
SplitLog.i(TAG, "Split %s has been loaded, ignore it!", splitInfo.getSplitName());
continue;
}
try {
SplitInfo.ApkData masterApkData = splitInfo.getApkDataForMaster();
SplitInfo.LibData libData = splitInfo.getPrimaryLibData(getContext());
String installedMark = splitInfo.obtainInstalledMark(getContext());
File splitLibDir = null;
if (libData != null) {
splitLibDir = SplitPathManager.require().getSplitLibDir(splitInfo, libData.getAbi());
}
boolean libBuiltIn = splitInfo.isBuiltIn() && masterApkData.getUrl().startsWith(SplitConstants.URL_NATIVE);
Intent splitFileIntent = createLastInstalledSplitFileIntent(libBuiltIn, installedMark, splitLibDir, splitInfo);
if (splitFileIntent != null) {
splitFileIntents.add(splitFileIntent);
}
SplitLog.i(TAG, "Split %s will work in process %s, %s it is %s",
splitInfo.getSplitName(), currentProcessName,
splitFileIntent == null ? "but" : "and",
splitFileIntent == null ? "not installed" : "installed");
} catch (IOException ignored) {
}
} else {
SplitLog.i(TAG, "Split %s do not need work in process %s", splitInfo.getSplitName(), currentProcessName);
}
}
return splitFileIntents;
}
首先校验当前包名是否符合Split的workProcess,然后再遍历需要加载的slitInfo,过滤已经加载过的。
获取masterApkData,json文件中定义的apk信息:
"apkData": [{
"abi": "master",
"url": "assets://qigsaw/java-master.zip",
"md5": "53a9fa31a54cddaf6dbc196be2255acd",
"size": 12822
}]
libData(lib包信息)
"libData": [{
"abi": "x86",
"jniLibs": [{
"name": "libhello-jni.so",
"md5": "960b507b9dd9d82b9b17b7912d0b7529",
"size": 5644
}]
}
libBuiltIn:是否为native工程。
然后把所有的参数都传给createLastInstalledSplitFileIntent方法(具体进一步生成Intent)
1.3 SplitLoadManagerImpl.createLastInstalledSplitFileIntent
该方法代码比较长就不贴了。可以边看代码边看本文。
首先获取部分File类型参数splitName,splitDir
markFile(分包标记文件/data/user/0/com.iqiyi.qigsaw.sample/app_qigsaw/1.0.0_a8414bd/java/1.1@1/53a9fa31a54cddaf6dbc196be2255acd.0)
specialMarkFile(特殊分包标记文件/data/user/0/com.iqiyi.qigsaw.sample/app_qigsaw/1.0.0_a8414bd/java/1.1@1/53a9fa31a54cddaf6dbc196be2255acd.0.ov,后面多了ov后缀,为了特殊兼容oppo,vivo手机)
以及splitApk,如果是native工程(libBuiltIn == true),则路径为:
/data/app/~~1qu9MQhn4Y836FdrHVHEqA==/com.iqiyi.qigsaw.sample-w-kwmFjt3MHzOHuoR5EMEA==/lib/arm64/libsplit_java.so
是普通工程则路径为:
/data/user/0/com.iqiyi.qigsaw.sample/app_qigsaw/1.0.0_a8414bd/java/1.1@1/java-master.apk
然后对ov手机做了特殊处理,检查oat文件是否为ELF格式,如果是的话尝试去创建markFile文件,否则删掉oat文件。(存疑)
接下来判断标记文件是否存在,如果存在则开始构建分包Intent。依次赋值Intent参数:
- SplitConstants.KET_NAME:插件Split名字
- SplitConstants.KEY_APK:插件apk路径
- SplitConstants.KEY_DEX_OPT_DIR:插件optimized dex路径
- SplitConstants.KEY_NATIVE_LIB_DIR:插件native lib路径
- SplitConstants.KEY_ADDED_DEX:插件已添加的 dev 路径
拿到Intent之后,回到loadInstalledSplitsInternal方法中,会判断splitFileIntents,如果splitFileIntents不为空,则会直接执行SplitLoadManagerImpl的加载方法:
createSplitLoadTask(splitFileIntents, null).run()。
1.4 预加载过程总结
预加载的过程主要是创建一个加载的Intent,里面包含了所需的各种参数。
2. 加载 (SplitLoadManagerImpl)
从createSplitLoadTask(splitFileIntents, null).run()开始分析真正的加载过程
2.1 SplitLoadManagerImpl.createSplitLoadTask
createSplitLoadTask方法有两个调用路径:
第一个是前文中的,QigsawApplication初始化的时候调用preloadInstalledSplits,最终执行加载任务
第二个是在SplitLoadSessionTask的run方法中,执行加载,调用链路为SplitLoadSessionTask ← SplitSessionLoaderImpl.load() ← SplitInstallListenerRegistry.onReceived()
通过SplitInstallListenerRegistry接收action为"com.iqiyi.android.play.core.splitinstall.receiver.SplitInstallUpdateIntentService"的动态广播触发加载任务,
该广播有两个发出链路,一个是SplitInstallService.onBinderDied()在安装服务的bundler销毁的时候触发,
另一个是SplitInstallSessionManagerImp.emitSessionState(),在安装器SplitApkInstaller中响应时机调用
然后回到createSplitLoadTask方法本身:
@Override
public Runnable createSplitLoadTask(List splitFileIntents, @Nullable onSplitLoadListener loadListener) {
List filterSplitFileIntentList = filterIntentsCanWorkInThisProcess(splitFileIntents);
if (filterSplitFileIntentList.isEmpty()) {
return new SkipSplitLoadTaskImpl();
}
if (splitLoadMode() == SplitLoad.MULTIPLE_CLASSLOADER) {
return new SplitLoadTaskImpl(this, filterSplitFileIntentList, loadListener);
} else {
return new SplitLoadTaskImpl2(this, filterSplitFileIntentList, loadListener);
}
}
首先根据WorkProcess做了过滤,然后判断是否是单classLoader加载模式执行不同的Task,那么SplitLoadTaskImpl和SplitLoadTaskImpl2有什么区别呢?
多classLoader模式下,SplitLoadTaskImpl是为每一个插件都创建了一个SplitDexClassLoader,并暂存在SplitApplicationLoaders中。
SplitLoadTaskImpl2是直接使用同一个SplitDexClassLoader进行加载。它们都是继承自SplitLoadTask
2.2 SplitLoadTask
SplitLoadTask是具体执行加载任务的task。
SplitLoadTask.run():
判断当前线程,如果是主线程,直接执行loadHandler.loadSplitsSync(this),否则post到主线程执行,并阻塞当前异步线程。
SplitLoadHandler.loadSplitsSync() 然后执行SplitLoadHandler.loadSplits()。
2.3 SplitLoadHandler.loadSplits()
具体的加载逻辑方法。
1.创建一个临时变量集合,Set
final class Split {
final String splitName;
final String splitApkPath;
Split(String splitName, String splitApkPath) {
this.splitName = splitName;
this.splitApkPath = splitApkPath;
}
@NonNull
@Override
public String toString() {
return "{" + splitName + "," + splitApkPath + "}";
}
}
包含了插件名和插件apk路径
2.遍历传入的intent集合splitFileIntents
3.分别解析前文1.3中提到的各种intent参数,apkpath,nativeLibPath,addedDexPaths等
结合前文Intent传递过来的各种参数,创建classLoader:
classLoader = splitLoader.loadCode(splitName,
addedDexPaths, dexOptPath == null ? null : new File(dexOptPath),
nativeLibPath == null ? null : new File(nativeLibPath),
info.getDependencies()
4.创建classLoader → SplitLoader.load() → SplitLoaderImpl.load() → SplitDexClassLoader.create()
SplitApplicationLoaders会存储所有split中的cl(classloader),其中单个的cl为SplitDexClassLoader
SplitDexClassLoader创建的时候会收集所有其他split中合格的cl,然后在自身findClass(或者lib,res)方法内部找不到的时候遍历它们,实现了兜底机制。
创建的时候会执行SplitUnKnownFileTypeDexLoader.loadDex(this, dexPaths, optimizedDirectory);兼容api21以下版本的so处理。
5.使用activator.createSplitApplication(classLoader, splitName)生成application
调用链路:SplitActivator.createSplitApplication → AABExtension.createApplication → AABExtensionManagerImpl.createApplication()
@Override
@SuppressLint("PrivateApi")
public Application createApplication(ClassLoader classLoader, String splitName) throws AABExtensionException {
Throwable error = null;
String applicationName = infoProvider.getSplitApplicationName(splitName);
if (!TextUtils.isEmpty(applicationName)) {
try {
Class> appClass = classLoader.loadClass(applicationName);
return (Application) appClass.newInstance();
} catch (ClassNotFoundException e) {
error = e;
} catch (InstantiationException e) {
error = e;
} catch (IllegalAccessException e) {
error = e;
}
}
if (error != null) {
throw new AABExtensionException(error);
}
return null;
}
通过反射私有api生成application实例。
6.activateSplit(splitName, splitApkPath, application, classLoader) 激活Split插件
splitLoader.loadResources(splitApkPath) : 加载资源
activator.attachSplitApplication(application); 通过aabExtension调用插件application的attach方法
activator.createAndActivateSplitContentProviders(classLoader, splitName) 通过aabExtension创建并激活ContentProviders
这里通过代理的cp:ContentProviderProxy创建真正的cp,ContentProviderProxy重写了attachInfo,保存了原本的名字,并存储到aabExtension中,
在加载的时候createAndActivateSplitContentProviders,通过cl反射原名来实现激活插件中原有的cp。
activator.invokeonCreateForSplitApplication(application); 通过aabExtension调用插件application的onCreate方法
7.将加载成功的插件加到SplitLoadManager,并回调加载成功。
2.4 加载过程总结
执行加载的过程主要在SplitLoadHandler.loadSplits()中。前面预加载的过程主要是做一些参数的准备工作,并生成一个Intent丢过来,在SplitLoadHandle中进行真正的加载过程,首先是构建application,然后构建插件Split,加载资源并调用插件Application的attach和onCreate方法。



