本文对爱奇艺框架Qigsaw对Feature库Application初始化流程和对ContentProvider的处理进行了分析。
《咏鹅 》
鹅,鹅,鹅,曲项向天歌,
白毛浮绿水,红掌拨清波。
-骆宾王
前言
在上一篇文章中我们对爱奇艺框架Qigsaw的插件部分进行了源码分析,今天来看下Qigsaw是如何完成Feature库Application的初始化和对ContentProvider的处理。
记录Feature库组件信息
在上一篇文章中分析Qigsaw插件部分源码时忽略了一个Transform,Qigsaw在application插件上注册了一个ComponentInfoTransform,用于创建一个记录Feature库组件信息的类,生成类如下,字段名格式为splitname+"_ACTIVITIES",其他组件类似,如果包含多个组件中间会以逗号分隔:
package com.iqiyi.android.qigsaw.core.extension;
public class ComponentInfo {
public static final String native_ACTIVITIES = "com.iqiyi.qigsaw.sample.ccode.NativeSampleActivity";
public static final String java_ACTIVITIES = "com.iqiyi.qigsaw.sample.java.JavaSampleActivity";
public static final String java_APPLICATION = "com.iqiyi.qigsaw.sample.java.JavaSampleApplication";
public ComponentInfo() {
}
}
这个类中记录了所有Feature库的三大组件Activity、Service、Receiver和Feature库的Application信息。这里并没有记录ContentProvider的信息,如果Feature库组件中包含ContentProvider,Qigsaw会创建一个ContentProvider的代理类,命名格式为providername+"_Decorated_"+splitname:
package com.iqiyi.qigsaw.sample.java;
import com.iqiyi.android.qigsaw.core.splitload.SplitContentProvider;
public class JavaContentProvider_Decorated_java extends SplitContentProvider {
public JavaContentProvider_Decorated_java() {
}
}
由于ContentProvider的初始化位于Application的attachBaseContext和onCreate之间,所以如果Feature库未安装并且Feature库中含有ContentProvider组件时肯定会造成ClassNotFound异常,所以Qigsaw会在打包过程中使用这个代理类替换真正的ContentProvider,避免由于Feature库未安装导致应用崩溃。
ComponentInfoTransform中的执行逻辑大致流程如下:
Feature库Application初始化流程
Feature库Application初始化流程分为两种情况,一种是Feature库已经安装打开应用时的初始化,另一种是执行安装命令后的Feature库Application初始化。
已安装的Feature库Application初始化流程
打开应用时Feature库的Application初始化流程大致如下:
这里的关键步骤是从系统中获取已安装的splitname,当API>=21时,从PackageInfo中获取splitname:
SplitAABInfoProvider.java
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
private String[] getSplitInstallInfo() {
try {
PackageInfo packageInfo;
return (packageInfo = context.getPackageManager().getPackageInfo(packageName, 0)) != null ? packageInfo.splitNames : null;
} catch (Throwable var2) {
SplitLog.printErrStackTrace(TAG, var2, "App is not found in PackageManager");
return null;
}
}
当API<21时,会从ApplicationInfo的metaData的shadow.bundletool.com.android.dynamic.apk.fused.modules
中获取splitname:
SplitAABInfoProvider.java
private Set<String> getFusedModules() {
Set<String> fusedModules = new HashSet<>();
ApplicationInfo appInfo;
try {
appInfo = context.getPackageManager().getApplicationInfo(packageName, PackageManager.GET_META_DATA);
} catch (Throwable e) {
SplitLog.printErrStackTrace(TAG, e, "App is not found in PackageManager");
return fusedModules;
}
if (appInfo != null && appInfo.metaData != null) {
String fusedName;
if ((fusedName = appInfo.metaData.getString("shadow.bundletool.com.android.dynamic.apk.fused.modules")) != null && !fusedName.isEmpty()) {
Collections.addAll(fusedModules, fusedName.split(",", -1));
fusedModules.remove("");
return fusedModules;
} else {
SplitLog.d(TAG, "App has no fused modules.");
return fusedModules;
}
} else {
SplitLog.d(TAG, "App has no applicationInfo or metaData");
return fusedModules;
}
}
然后根据splitname从刚才ComponentInfoTransform中生成的ComponentInfo类中反射获取对应的ApplicationName,然后使用ClassLoader完成初始化,在Application的onCreate中调用Feature库的Application的onCreate,至此打开应用时Feature库的Application初始化流程已经完成。
安装时Feature库Application初始化流程
了解了已安装时Feature库Application的初始化流程,再来看安装时Feature库Application初始化流程就比较简单了,安装时会传入splitname等信息,会先判断该split是否已经安装过,如果安装过就会直接跳过,如果没有安装过会根据splitname从ComponentInfo类中反射获取对应的ApplicationName,然后使用ClassLoader完成初始化。
除了Application初始化还会对真正的ContentProvider完成初始化。
ContentProviderProxy.java
public abstract class SplitContentProvider extends ContentProviderProxy {
...
}
public abstract class ContentProviderProxy extends ContentProvider {
...
@Override
public void attachInfo(Context context, ProviderInfo info) {
String className = getClass().getName();
String[] cuts = className.split(NAME_INFIX);
this.realContentProviderClassName = cuts[0];
this.splitName = cuts[1];
super.attachInfo(context, info);
this.providerInfo = new ProviderInfo(info);
AABExtension.getInstance().put(splitName, this); //在hashmap中记录所有代理ContentProvider,用于split安装后激活真正的provider
}
...
}
AABExtension.java
void put(String splitName, ContentProviderProxy providerProxy) {
List<ContentProviderProxy> providerProxies = sSplitContentProviderMap.get(splitName);
if (providerProxies == null) {
providerProxies = new ArrayList<>();
sSplitContentProviderMap.put(splitName, providerProxies);
}
providerProxies.add(providerProxy);
}
在ContentProviderProxy代理类中会将该代理类记录在AABExtension类中的hashmap中,然后在Feature库Application完成初始化后对真正的ContentProvider完成初始化。
总结
首先介绍了下ComponentInfoTransform的作用用于创建一个记录所有Feature库组件的类,针对ContentProvider会创建一个代理类,在打包时会将清单文件中真正的ContentProvider替换为代理类,避免Feature库未安装时导致ClassNotFound异常。针对已安装的Feature在应用打开时会从系统获取splitname,然后根据splitname从创建的ComponentInfo中获取对应applicationname,最后使用ClassLoader完成Feature库的Application的加载和初始化;安装时除了Feature库的Application的初始化还会对真正的ContentProvider完成初始化。
网友评论