1、VirtualAPK的接入
1.1、宿主工程引入VirtualApk
- 在项目Project的build.gradle中添加依赖
dependencies {
classpath 'com.didi.virtualapk:gradle:0.9.8.6'
}
- 在宿主app的build.gradle中引入VirtualApk的host插件
apply plugin: 'com.didi.virtualapk.host'
- 在app中添加依赖
dependencies {
implementation 'com.didi.virtualapk:core:0.9.8'
}
- 在Application中完成PluginManager的初始化
public class VirtualApplication extends Application {
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
PluginManager.getInstance(base).init();
}
}
1.2、配置插件Apk
- 在插件project中配置
classpath 'com.didi.virtualapk:gradle:0.9.8.6'
- 在插件app的build.gradle中引入plugin插件
apply plugin: 'com.didi.virtualapk.plugin'
- 配置插件信息和版本
virtualApk{
// 插件资源表中的packageId,需要确保不同插件有不同的packageId
// 范围 0x1f - 0x7f
packageId = 0x6f
// 宿主工程application模块的路径,插件的构建需要依赖这个路径
// targetHost可以设置绝对路径或相对路径
targetHost = '../../../VirtualAPkDemo/app'
// 默认为true,如果插件有引用宿主的类,那么这个选项可以使得插件和宿主保持混淆一致
//这个标志会在加载插件时起作用
applyHostMapping = true
}
- 设置签名(Virtual仅支持Release,host项目和plugin项目签名一致)
signingConfigs {
release {
storeFile file('/Users/wuliangliang/AndroidSubjectStudyProject/PluginProject/VirtualAPkDemo/keystore/keystore')
storePassword '123456'
keyAlias = 'key'
keyPassword '123456'
}
}
buildTypes {
release {
signingConfig signingConfigs.release
}
}
1.3、执行生成插件Plugin
-
执行assemablePlugin 产生Plugin文件
-
将插件Plugin安装到手机中
adb push ./app/build/outputs/plugin/release/com.alex.kotlin.virtualplugin_20190729172001.apk /sdcard/plugin_test.apk
- 在插件Plugin项目中所有的四大组件的使用都和原生使用方法一致
- 注意问题
- 要先构建一次宿主app,才可以构建plugin,否则异常
- 插件布局文件中要设置资源的ID,否则异常:Cannot get property 'id' on null object
- plugin 增加 gradle.properties 文件并配置android.useDexArchive=false,否则异常
1.4、在宿主程序中使用插件Plugin
- 在宿主App中加载插件apk
private void loadApk() {
File apkFile = new File(Environment.getExternalStorageDirectory(), "Test.apk");
if (apkFile.exists()) {
try {
PluginManager.getInstance(this).loadPlugin(apkFile);
} catch (Exception e) {
e.printStackTrace();
}
}
}
在插件下载或安装到设备后,获取插件的文件,调用PluginManager.loadPlugin()加载插件,PluginManager会完成所有的代码解析和资源加载,详细内容后面的源码分析; 2. 执行界面跳转至插件中
final String pkg = "com.alex.kotlin.virtualplugin”; //插件Plugin的包名
Intent intent = new Intent();
intent.setClassName(pkg, "com.alex.kotlin.virtualplugin.MainPluginActivity”); //目标Activity的全路径
startActivity(intent);
2、Virtual APK 源码分析
2.1、PluginManager初始化
- 在Application中添加VirtualApk初始化
PluginManager.getInstance(base).init();
- PluginManager.getInstance(base):创建PluginManager实例,单例对外提供
public static PluginManager getInstance(Context base) {
if (sInstance == null) {
synchronized (PluginManager.class) {
if (sInstance == null) {
sInstance = createInstance(base); // 调用createInstance()方法创建PluginManager,单例对外提供
}
}
}
return sInstance;
}
- createInstance(base):创建PluginManager对象
private static PluginManager createInstance(Context context) {
try {
//1、获取metaData
Bundle metaData = context.getPackageManager()
.getApplicationInfo(context.getPackageName(), PackageManager.GET_META_DATA)
.metaData;
if (metaData == null) {
return new PluginManager(context); //2、
}
String factoryClass = metaData.getString("VA_FACTORY”); //3、此处获取的是什么?
if (factoryClass == null) {
return new PluginManager(context);
}
//4、创建PluginManager
PluginManager pluginManager = Reflector.on(factoryClass).method("create", Context.class).call(context);
if (pluginManager != null) {
return pluginManager;
}
} catch (Exception e) {
Log.w(TAG, "Created the instance error!", e);
}
return new PluginManager(context);
}
createInstance()执行逻辑:
- 从宿主Context中解析APk中的metaData
- 如果metaData为null,则直接使用context创建PluginManager
- 从metaData中获取factoryClass,并反射创建PluginManager,否则直接创建PluginManager
- PluginManager的构造函数
protected PluginManager(Context context) {
if (context instanceof Application) { // 1、
this.mApplication = (Application) context;
this.mContext = mApplication.getBaseContext();
} else {
final Context app = context.getApplicationContext();
if (app == null) {
this.mContext = context;
this.mApplication = ActivityThread.currentApplication();
} else {
this.mApplication = (Application) app;
this.mContext = mApplication.getBaseContext();
}
}
mComponentsHandler = createComponentsHandler(); //2、
hookCurrentProcess(); //3、
}
PluginManager()执行流程:
- 根据context类型,分别进行获取并赋值mApplication & mContext
- 创建ComponentsHandler对象,ComponentsHandler的作用是作为工具类,用于处理Intent和Service服务,关于ComponentsHandler后面会再提到
- Hook Instrumentation和SystemServices(主要使用Hook技术)
- hookCurrentProcess()
protected void hookCurrentProcess() {
hookInstrumentationAndHandler();
hookSystemServices();
}
- hookInstrumentationAndHandler():Hook Activity启动中使用的Instrumentation和Handler的CallBack,关于Handler的Callback查看Android消息队列
ActivityThread activityThread = ActivityThread.currentActivityThread();
Instrumentation baseInstrumentation = activityThread.getInstrumentation(); //1、
final VAInstrumentation instrumentation = createInstrumentation(baseInstrumentation); //2、
//3、
Reflector.with(activityThread).field("mInstrumentation").set(instrumentation);
//4、
Handler mainHandler = Reflector.with(activityThread).method("getHandler").call();
Reflector.with(mainHandler).field("mCallback").set(instrumentation);
this.mInstrumentation = instrumentation; // 赋值 PluginManager的mInstrumentation
执行流程:
- 从ActivityThread中获取的系统中的Instrumentation对象
- 创建代理的Instrumentation对象,内部保存原对象
- 反射设置代理的VAInstrumentation对象到ActivityThread中
- 同样Hook替换 ActivityThread 中的类H(Handler)中的mCallback,拦截 handleMessage()的回调逻辑
- hookSystemServices():Hook系统的IActivityManager服务
Singleton<IActivityManager> defaultSingleton;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {//1、
//8.0 以上获取IActivityManagerSingleton,采用AIDL获取AMS
defaultSingleton = Reflector.on(ActivityManager.class).field("IActivityManagerSingleton").get();
} else {
//8.0 之前获取gDefault,使用代理执行AMS
defaultSingleton = Reflector.on(ActivityManagerNative.class).field("gDefault").get();
}
IActivityManager origin = defaultSingleton.get(); //2
IActivityManager activityManagerProxy = (IActivityManager) Proxy.newProxyInstance(mContext.getClassLoader(), new Class[]{IActivityManager.class},createActivityManagerProxy(origin));//3
Reflector.with(defaultSingleton).field("mInstance").set(activityManagerProxy); // 4、
if (defaultSingleton.get() == activityManagerProxy) {
this.mActivityManager = activityManagerProxy;//5、
}
hook系统服务在Hook 技术 文章中已经接收过程了,这里简单介绍逻辑:
- 根据Android版本的处理方式不同 ,8.0 以上获取IActivityManagerSingleton,采用AIDL获取AMS,8.0 之前获取gDefault,使用代理执行AMS
- 反射获取系统中原类的IActivityManager的代理类;
- 动态代理IActivityManager接口,此处创建的是ActivityManagerProxy对象;
- 反射设置代理的IActivityManager实例到系统中;
- 获取设置的代理的Proxy,保存在mActivityManager中;
由四大组件启动过程源码分析,Hook了Instrumentation对象就可以完成对Activity的创建过程的修改,Hook了系统的IActivityManager可以实现对AMS工作的拦截,而AMS对四大组件的整个工作过程至关重要,也就是说我们已经掌控了四大组件;
2.2、加载Plugin插件
- loadPlugin()
public void loadPlugin(File apk) throws Exception {
//1、
LoadedPlugin plugin = createLoadedPlugin(apk);
//2、
this.mPlugins.put(plugin.getPackageName(), plugin);
}
loadPlugin()中主要完成两件事:
- 根据传入的apk,创建LoadedPlugin对象
- 在mPlugins中根据包名保存创建的对象,使用时直接根据插件的包名获取
- LoadedPlugin:在构造函数中完成了大量的数据操作,具体细节见注释,下面逐步分析VirtualAPK是如何加载插件的
public LoadedPlugin(PluginManager pluginManager, Context context, File apk) throws Exception {
//1、保存安装包的APk路径、pluginManager、context
this.mPluginManager = pluginManager;
this.mHostContext = context;
this.mLocation = apk.getAbsolutePath();
//2、解析Plugin的AndroidManifest.xml文件
this.mPackage = PackageParserCompat.parsePackage(context, apk, PackageParser.PARSE_MUST_BE_APK);
this.mPackage.applicationInfo.metaData = this.mPackage.mAppMetaData;
//3、创建并实例化PackageInfo对象
this.mPackageInfo = new PackageInfo();
this.mPackageInfo.applicationInfo = this.mPackage.applicationInfo;
this.mPackageInfo.applicationInfo.sourceDir = apk.getAbsolutePath();
if (Build.VERSION.SDK_INT >= 28
|| (Build.VERSION.SDK_INT == 27 && Build.VERSION.PREVIEW_SDK_INT != 0)) {
try {
this.mPackageInfo.signatures = this.mPackage.mSigningDetails.signatures;
} catch (Throwable e) {
PackageInfo info = context.getPackageManager().getPackageInfo(context.getPackageName(), PackageManager.GET_SIGNATURES);
this.mPackageInfo.signatures = info.signatures;
}
} else {
this.mPackageInfo.signatures = this.mPackage.mSignatures;
}
//保存包名
this.mPackageInfo.packageName = this.mPackage.packageName;
this.mPackageInfo.versionCode = this.mPackage.mVersionCode;
this.mPackageInfo.versionName = this.mPackage.mVersionName;
this.mPackageInfo.permissions = new PermissionInfo[0];
//4、创建插件中自己的PackManager
this.mPackageManager = createPluginPackageManager();
//5、创建插件中自己的PluginContext
this.mPluginContext = createPluginContext(null);
this.mNativeLibDir = getDir(context, Constants.NATIVE_DIR);
//获取so文件路径
this.mPackage.applicationInfo.nativeLibraryDir = this.mNativeLibDir.getAbsolutePath();
//6、创建Resource和ClassLoader对象
this.mResources = createResources(context, getPackageName(), apk);
//7、创建ClassLoader对象
this.mClassLoader = createClassLoader(context, apk, this.mNativeLibDir, context.getClassLoader());
tryToCopyNativeLib(apk);
Map<ComponentName, ActivityInfo> activityInfos = new HashMap<ComponentName, ActivityInfo>();
for (PackageParser.Activity activity : this.mPackage.activities) {
activity.info.metaData = activity.metaData;
activityInfos.put(activity.getComponentName(), activity.info); // 8、缓存插件中解析的Activity
}
this.mPackageInfo.activities = activityInfos.values().toArray(new ActivityInfo[activityInfos.size()]);
Map<ComponentName, ServiceInfo> serviceInfos = new HashMap<ComponentName, ServiceInfo>();
for (PackageParser.Service service : this.mPackage.services) {
serviceInfos.put(service.getComponentName(), service.info); // 9、缓存插件中解析的服务Service
}
this.mPackageInfo.services = serviceInfos.values().toArray(new ServiceInfo[serviceInfos.size()]);
Map<String, ProviderInfo> providers = new HashMap<String, ProviderInfo>();
Map<ComponentName, ProviderInfo> providerInfos = new HashMap<ComponentName, ProviderInfo>();
for (PackageParser.Provider provider : this.mPackage.providers) {
providers.put(provider.info.authority, provider.info);
providerInfos.put(provider.getComponentName(), provider.info); // 10、缓存插件中解析的ContentProvider
}
this.mPackageInfo.providers = providerInfos.values().toArray(new ProviderInfo[providerInfos.size()]);
// 11、缓存插件中解析的广播,对于静态注册的广播改为动态注册
Map<ComponentName, ActivityInfo> receivers = new HashMap<ComponentName, ActivityInfo>();
for (PackageParser.Activity receiver : this.mPackage.receivers) {
receivers.put(receiver.getComponentName(), receiver.info);
BroadcastReceiver br = BroadcastReceiver.class.cast(getClassLoader().loadClass(receiver.getComponentName().getClassName()).newInstance());
for (PackageParser.ActivityIntentInfo aii : receiver.intents) {
this.mHostContext.registerReceiver(br, aii); // 注册广播
}
}
this.mPackageInfo.receivers = receivers.values().toArray(new ActivityInfo[receivers.size()]);
invokeApplication(); // 12、初始化Plugin的Application
}
2.2.1、创建插件中的PackManager
protected class PluginPackageManager extends PackageManager {
protected PackageManager mHostPackageManager = mHostContext.getPackageManager(); //1
@Override
public PackageInfo getPackageInfo(String packageName, int flags) throws NameNotFoundException {
LoadedPlugin plugin = mPluginManager.getLoadedPlugin(packageName); // 2
if (null != plugin) {
return plugin.mPackageInfo; // 3
}
return this.mHostPackageManager.getPackageInfo(packageName, flags); //4
}
}
PluginPackageManager继成PackageManager重些其中的方法,在调用方法时判断获取的是插件APK还是宿主APK,并对应处理返回,具体如下:
- PluginPackageManager中保存宿主的PackageManager对象
- 在查找信息时根据包名获取缓存的LoadedPlugin对象
- 对于获取插件中的信息,返回插件的PackageInfo对象
- 如果根据包名获取的LoadedPlugin = null,返回宿主的PackageInfo对象
- 创建插件中自己的PluginContext对象 this.mPluginContext = createPluginContext(null);
2.2.2 创建PluginContext对象
public PluginContext createPluginContext(Context context) {
if (context == null) {
return new PluginContext(this); //1、由前面知道传入contetx为null,执行此处传递LoadedPlugin对象
}
return new PluginContext(this, context);
}
- PluginContext:关于Context详情点击查看Android插件化——Context机制
class PluginContext extends ContextWrapper { // 1、继承ContextWrapper,用于在Activity中使用替换原来的Context
private final LoadedPlugin mPlugin;
public PluginContext(LoadedPlugin plugin) {
super(plugin.getPluginManager().getHostContext()); // 赋值mBase对象,此处使用HostContext
this.mPlugin = plugin; // 保存插件创建的Plugin对象
}
@Override
public Context getApplicationContext() {
return this.mPlugin.getApplication();
}
private Context getHostContext() {
return getBaseContext();
}
@Override
public ContentResolver getContentResolver() {
return new PluginContentResolver(getHostContext());
}
@Override
public ClassLoader getClassLoader() {
return this.mPlugin.getClassLoader();
}
@Override
public Resources getResources() {
return this.mPlugin.getResources();
}
@Override
public void startActivity(Intent intent) {
// 重写startActivity处理Intent
ComponentsHandler componentsHandler = mPlugin.getPluginManager().getComponentsHandler();
componentsHandler.transformIntentToExplicitAsNeeded(intent);
super.startActivity(intent);
}
}
在Activity中一些资源和服务都是通过Context获取的,而Context是ContextWrapper的子类,在PluginContext中同样继承ContextWrapper,重写一系列的Context方法,在加载插件时使用PluginContext替代插件APK中的Context、控制插件中资源和一些类的获取,例如代码中的getResources()、getContentResolver()等;
- createResources():创建Resource对象
protected Resources createResources(Context context, String packageName, File apk) throws Exception {
if (Constants.COMBINE_RESOURCES) {// 1
return ResourcesManager.createResources(context, packageName, apk); // 2
} else {
Resources hostResources = context.getResources();
AssetManager assetManager = createAssetManager(context, apk);
return new Resources(assetManager, hostResources.getDisplayMetrics(), hostResources.getConfiguration());//3
}
}
由Android系统知道,程序在启动时将资源加载到程序对应的Resource中,此处就是收动将插件apk中的资源加载到resource中,执行流程:
- 根据COMBINE_RESOURCES判断是否合并插件资源,这里的COMBINE_RESOURCES就是在插件项目中配置的applyHostMapping,对于插件中需要使用宿主资源的需要合并;
- 调用ResourcesManager.createResources()创建合并资源的Resource
- 对插件创建单独的AssetManager对象加载插件apk路径,然后创建Resources实例
- ResourcesManager.createResources():创建合并的Resource对象
public static synchronized Resources createResources(Context hostContext, String packageName, File apk) throws Exception {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { //1、处理7.0 之后 Resource的创建
return createResourcesForN(hostContext, packageName, apk);
}
//2、处理7.0 之前,使用AssetManager添加资源
Resources resources = ResourcesManager.createResourcesSimple(hostContext, apk.getAbsolutePath());
ResourcesManager.hookResources(hostContext, resources);
return resources;
}
- createResourcesForN():处理7.0 之后 Resource的创建
private static Resources createResourcesForN(Context context, String packageName, File apk) throws Exception {
String newAssetPath = apk.getAbsolutePath(); // Plugin apk的路径
ApplicationInfo info = context.getApplicationInfo();
String baseResDir = info.publicSourceDir; // host的文件路径
info.splitSourceDirs = append(info.splitSourceDirs, newAssetPath); //2、
LoadedApk loadedApk = Reflector.with(context).field("mPackageInfo").get(); //3、
Reflector rLoadedApk = Reflector.with(loadedApk).field("mSplitResDirs");
String[] splitResDirs = rLoadedApk.get();
rLoadedApk.set(append(splitResDirs, newAssetPath));
final ResourcesManager resourcesManager = android.app.ResourcesManager.getInstance();
ArrayMap<ResourcesKey, WeakReference<ResourcesImpl>> originalMap = //4、Reflector.with(resourcesManager).field("mResourceImpls").get();
synchronized (resourcesManager) {
HashMap<ResourcesKey, WeakReference<ResourcesImpl>> resolvedMap = new HashMap<>();
if (Build.VERSION.SDK_INT >= 28
|| (Build.VERSION.SDK_INT == 27 && Build.VERSION.PREVIEW_SDK_INT != 0)) { // P Preview
ResourcesManagerCompatForP.resolveResourcesImplMap(originalMap, resolvedMap, context, loadedApk);
} else {
ResourcesManagerCompatForN.resolveResourcesImplMap(originalMap, resolvedMap, baseResDir, newAssetPath);
}
originalMap.clear();
originalMap.putAll(resolvedMap);
}
android.app.ResourcesManager.getInstance().appendLibAssetForMainAssetPath(baseResDir, packageName + ".vastub");
//5、
Resources newResources = context.getResources();
for (LoadedPlugin plugin : PluginManager.getInstance(context).getAllLoadedPlugins()) {
plugin.updateResources(newResources);
}
return newResources;
}
Resource的创建流程:
- 分别获取Plugin apk和host apk中的资源路径
- 获取当前项目中的splitSourceDirs,splitSourceDirs是保存当前程序所有已经加载的资源路径,按索引顺序获取多个拆分apk的完整路径,并检查是否已经包含插件的路径,对于未添加的将apk路径添加进去
- 反射给PackageInfo设置合并之后的splitSourceDirs,此时表示将插件资源路径添加进去了
- 获取ResourceImpl及其配置的映射
- 获取添加插件资源路径后的Resource对象,并更新每个插件apk对应的LoadedPlugin中的Resources
- . 处理Android 7.0之前的Resource创建
Resources resources = ResourcesManager.createResourcesSimple(hostContext, apk.getAbsolutePath());//创建Resource对象
ResourcesManager.hookResources(hostContext, resources); // 反射更新设置宿主Context中的resource
private static Resources createResourcesSimple(Context hostContext, String apk) throws Exception {
Resources hostResources = hostContext.getResources();
Resources newResources = null;
AssetManager assetManager;
Reflector reflector = Reflector.on(AssetManager.class).method("addAssetPath", String.class); //1、
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { // Android 5.0 之前
assetManager = AssetManager.class.newInstance();
reflector.bind(assetManager);
//2、
final int cookie1 = reflector.call(hostContext.getApplicationInfo().sourceDir);
} else {
assetManager = hostResources.getAssets(); //3、
reflector.bind(assetManager);
}
final int cookie2 = reflector.call(apk); // 4、
List<LoadedPlugin> pluginList = PluginManager.getInstance(hostContext).getAllLoadedPlugins();
for (LoadedPlugin plugin : pluginList) { // 5、
final int cookie3 = reflector.call(plugin.getLocation());
}
//6、适配不同的手机类型,创建新的Resource对象
if (isMiUi(hostResources)) {
newResources = MiUiResourcesCompat.createResources(hostResources, assetManager);
} else if (isVivo(hostResources)) {
newResources = VivoResourcesCompat.createResources(hostContext, hostResources, assetManager);
} else if (isNubia(hostResources)) {
newResources = NubiaResourcesCompat.createResources(hostResources, assetManager);
} else if (isNotRawResources(hostResources)) {
newResources = AdaptationResourcesCompat.createResources(hostResources, assetManager);
} else {
newResources = new Resources(assetManager, hostResources.getDisplayMetrics(), hostResources.getConfiguration());
}
// 7、将所有LoadedPlugin同步到新的newResources
for (LoadedPlugin plugin : pluginList) {
plugin.updateResources(newResources);
}
return newResources;
}
实现原理:创建一个新的AssetManager对象,使用AssetManager的addAssetPath()依次将所有插件和宿主中的资源路径添加到对象中,根据新的AssetManager对象创建Resource对象,具体细节:
- 反射获取Assetmanager的addAssetPath方法
- 对于Android 5.0之前,将host中的资源路径保存在新创建的AssetManager中 ,5.0以后直接从context中获取AssetManager对象
- 将Plugin中的资源路径添加到AssetManager对象
- 遍历所有插件,将所有资源添加到同一个AssetManager对象
- 适配不同的手机类型,创建新的Resource对象
- 将所有LoadedPlugin同步到新的newResources
2.2.3 创建ClassLoader对象
protected ClassLoader createClassLoader(Context context, File apk, File libsDir, ClassLoader parent) throws Exception {
File dexOutputDir = getDir(context, Constants.OPTIMIZE_DIR);
String dexOutputPath = dexOutputDir.getAbsolutePath();//1、获取原APP中的dex文件路径
//2、创建DexClassLoader对象,同时加载host已经存在的dex文件和插件apk中的文件
DexClassLoader loader = new DexClassLoader(apk.getAbsolutePath(), dexOutputPath, libsDir.getAbsolutePath(), parent);
if (Constants.COMBINE_CLASSLOADER) {
DexUtil.insertDex(loader, parent, libsDir);//3、将所有的dex资源合并到宿主的ClassLoader中
}
return loader;
}
createClassLoader()中先获取当前app和插件的dex文件路径,然后创建DexClassLoader同时加载两个文件中的所有资源,在调用DexUtil.insertDex()合并dex资源,关于ClassLoader不了解的点击Android热修复之路(一)——ClassLoader
- DexUtil.insertDex():合并宿主和插件的dex、so库中的类文件,具体细节件注释和上面ClassLoader连接
public static void insertDex(DexClassLoader dexClassLoader, ClassLoader baseClassLoader, File nativeLibsDir) throws Exception {
Object baseDexElements = getDexElements(getPathList(baseClassLoader)); // 1、获取传入的host默认的ClassLoader中加载的Elements集合
Object newDexElements = getDexElements(getPathList(dexClassLoader)); //2、获取dexClassLoader中加载的Elements(host & plugin)
Object allDexElements = combineArray(baseDexElements, newDexElements); // 3、合并Elements
Object pathList = getPathList(baseClassLoader);
Reflector.with(pathList).field("dexElements").set(allDexElements); // 4、反射设置合并后的Elements集合
insertNativeLibrary(dexClassLoader, baseClassLoader, nativeLibsDir);
}
2.2.4 解析插件apk
- 解析PackageInfo信息
this.mPackage = PackageParserCompat.parsePackage(context, apk, PackageParser.PARSE_MUST_BE_APK);
this.mPackage.applicationInfo.metaData = this.mPackage.mAppMetaData;
//3、创建并实例化PackageInfo对象
this.mPackageInfo = new PackageInfo();
- 缓存插件中的Activity、Service、Provider
- 缓存并注册插件中的静态广播
Map<ComponentName, ActivityInfo> receivers = new HashMap<ComponentName, ActivityInfo>();
for (PackageParser.Activity receiver : this.mPackage.receivers) {
receivers.put(receiver.getComponentName(), receiver.info);
BroadcastReceiver br = BroadcastReceiver.class.cast(getClassLoader().loadClass(receiver.getComponentName().getClassName()).newInstance());
for (PackageParser.ActivityIntentInfo aii : receiver.intents) {
this.mHostContext.registerReceiver(br, aii);
}
}
2.2.5 初始化Plugin的Application
public void invokeApplication() throws Exception {
final Exception[] temp = new Exception[1];
RunUtil.runOnUiThread(new Runnable() {
@Override
public void run() {
try {
mApplication = makeApplication(false, mPluginManager.getInstrumentation()); //
} catch (Exception e) {
temp[0] = e;
}
}
}, true);
}
- makeApplication():创建Application对象
protected Application makeApplication(boolean forceDefaultAppClass, Instrumentation instrumentation) throws Exception {
String appClass = this.mPackage.applicationInfo.className;
if (forceDefaultAppClass || null == appClass) {
appClass = "android.app.Application”; //设置执行的类
}
//
this.mApplication = instrumentation.newApplication(this.mClassLoader, appClass, this.getPluginContext());
mApplication.registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacksProxy());
//调用Application的onCreate(),此处执行的是插件APK中的Application
instrumentation.callApplicationOnCreate(this.mApplication);
return this.mApplication;
}
在makeApplication()内部调用instrumentation.newApplication()创建Application对象和执行Application的onCreate(),关于instrumentation中如何创建的参考(四大组件的启动过程)
3、Activity插件化加载
- 实现原理:根据四大组件之Activity的工作流程知道,Activity启动过程中调用Instrumentation中的方法,而VirtualApk中使用VAInstrumentation代理了Instrumentation对象,VAInstrumentation在启动插件Activity过程中,首先替换为空的占位Activity骗过系统的检查,然后在创建Activity实例时创建原来的Activity,从而达到启动插件的效果
3.1、 VAInstrumentation
Activity在启动过程中调用Instrumentation的execStartActivity(),在VAInstrumentation中重写此方法拦截启动Activity过程,在此方法中修改处理Intent
@Override
public ActivityResult execStartActivity(Context who, IBinder contextThread, IBinder token, Activity target, Intent intent, int requestCode) {
injectIntent(intent); //1、
return mBase.execStartActivity(who, contextThread, token, target, intent, requestCode); //2、调用原来逻辑继续执行
}
- . injectIntent(intent):更改原请求的Intent对象
protected void injectIntent(Intent intent) {
//此处调用的getComponentsHandler()就是拿到前面创建的ComponentsHandler对象
mPluginManager.getComponentsHandler().transformIntentToExplicitAsNeeded(intent);
if (intent.getComponent() != null) {
this.mPluginManager.getComponentsHandler().markIntentIfNeeded(intent);
}
}
- . transformIntentToExplicitAsNeeded():遍历插件中解析缓存的Activity集合,查询此Intent启动哪个插件的Activity,并获取插件中的ResolveInfo信息
public Intent transformIntentToExplicitAsNeeded(Intent intent) {
ComponentName component = intent.getComponent();
if (component == null || component.getPackageName().equals(mContext.getPackageName())) {
ResolveInfo info = mPluginManager.resolveActivity(intent); // 1
if (info != null && info.activityInfo != null) {
component = new ComponentName(info.activityInfo.packageName, info.activityInfo.name); //2
intent.setComponent(component);
}
}
return intent;
}
- 调用mPluginManager.resolveActivity()遍历查找处理Intent的插件,从而获取插件中对应Activity的信息
- 使用查询到的Activity信息,创建ComponentName设置启动的包名和Activity的类名,并设置到Intent中
- . markIntentIfNeeded():匹配VirtualApk中设置的占位Activity
public void markIntentIfNeeded(Intent intent) {
String targetPackageName = intent.getComponent().getPackageName(); //1、 名
String targetClassName = intent.getComponent().getClassName();
if (!targetPackageName.equals(mContext.getPackageName()) && mPluginManager.getLoadedPlugin(targetPackageName) != null) {
intent.putExtra(Constants.KEY_IS_PLUGIN, true); //2、
intent.putExtra(Constants.KEY_TARGET_PACKAGE, targetPackageName);
intent.putExtra(Constants.KEY_TARGET_ACTIVITY, targetClassName);
dispatchStubActivity(intent); //3、
}
}
在markIntentIfNeeded()中:
- 获取Intent中设置启动Activity的包名和类
- 在Intent中保存真实启动Activity的包名和类名
- 调用dispatchStubActivity()查找匹配的占位Activity
- dispatchStubActivity()
private void dispatchStubActivity(Intent intent) {
ComponentName component = intent.getComponent();
String targetClassName = intent.getComponent().getClassName();
LoadedPlugin loadedPlugin = mPluginManager.getLoadedPlugin(intent); // 1、
ActivityInfo info = loadedPlugin.getActivityInfo(component); //2、
int launchMode = info.launchMode; // 3、
Resources.Theme themeObj = loadedPlugin.getResources().newTheme();
themeObj.applyStyle(info.theme, true);
//4、
String stubActivity = mStubActivityInfo.getStubActivity(targetClassName, launchMode, themeObj);
intent.setClassName(mContext, stubActivity); //5、
}
在VirtualAPk的注册清单中提前注册好了不同条件的占位Activity,dispatchStubActivity()中主要从VirtualAPk中选择合适的占位Activity:
- 获取启动插件包对应的LoadedPlugin对象
- 获取对应Activity的ActivityInfo信息
- 获取Activity的启动模式和主题
- 根据启动模式和主题查找占位Activity对象
- 在Intent中修改目标Activity为占位Activity
3.2、 创建真正Activity的实例
- 在VAInstrumentation中重写newActivity()方法
public Activity newActivity(ClassLoader cl, String className, Intent intent) throws InstantiationException, IllegalAccessException, ClassNotFoundException {
try {
cl.loadClass(className); // 1、ClassLoader加载类名
} catch (ClassNotFoundException e) { // 2、抛出ClassNotFoundException,表示启动插件Activity
ComponentName component = PluginUtil.getComponent(intent);
String targetClassName = component.getClassName(); //3、
LoadedPlugin plugin = this.mPluginManager.getLoadedPlugin(component);
// 4、创建Plugin的Activity真正的实例
Activity activity = mBase.newActivity(plugin.getClassLoader(), targetClassName, intent);
activity.setIntent(intent);
//5、
Reflector.QuietReflector.with(activity).field("mResources").set(plugin.getResources());
return newActivity(activity);
}
return newActivity(mBase.newActivity(cl, className, intent)); // 对于ClassLoader中查找到的直接调用原来方法初始化Activity
}
在Activity的启动过程中会回调newActivity()创建对象,所以在VAInstrumentation重写此方法:
- 调用cl.loadClass(className)查找启动的Activity,如果抛出异常即表示启动的是插件Activity
- 从启动意图Intent中获取目标Activity的类名,并从PluginManager中获取插件对应的LoadedPlugin对象
- 利用Plugin中创建的ClassLoader创建出Activity的实例
- 替换插件Activity的mResources对象为Plugin中创建的Resource,从而改变Context资源和类的获取
4、Service 插件化加载
- 插件原理:通过Hook系统服务代理IActivityManager,从而拦截到启动和停止服务、解除或绑定服务等操作,拦截操作后启动插件中设置的代理Service,通过本地代理服务避免了插件服务的清单文件注册检查,在启动的Service中代理分发插件Service,从而实现调用插件Service中的方法;
- 关于Service的启动流程参见L5、四大组件的启动过程
- ActivityManagerProxy:IActivityManager的代理类
public ActivityManagerProxy(PluginManager pluginManager, IActivityManager activityManager) {
this.mPluginManager = pluginManager;
this.mActivityManager = activityManager;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if ("startService".equals(method.getName())) {
try {
return startService(proxy, method, args); // 处理启动服务
}
} else if ("stopService".equals(method.getName())) {
try {
return stopService(proxy, method, args); // 处理停止服务
}
} else if ("stopServiceToken".equals(method.getName())) {
try {
return stopServiceToken(proxy, method, args);
}
} else if ("bindService".equals(method.getName())) {
try {
return bindService(proxy, method, args); // 处理绑定服务
}
} else if ("unbindService".equals(method.getName())) {
try {
return unbindService(proxy, method, args); // 处理解绑服务
}
}
try {
return method.invoke(this.mActivityManager, args);
}
}
由前面Pluginmanager初始化过程知道VirtualAPk Hook了系统的AMS,Hook中传递的Binder对象就是ActivityManagerProxy对象,所以AMS对Service的任何操作都会经过此处的invoke(),我们就可以在invoke()方法中干涉启动过程;
- 启动服务
- startService():处理启动服务
protected Object startService(Object proxy, Method method, Object[] args) throws Throwable {
IApplicationThread appThread = (IApplicationThread) args[0]; // 1、
Intent target = (Intent) args[1];
ResolveInfo resolveInfo = this.mPluginManager.resolveService(target, 0);//2、查找处理Intent,务
if (null == resolveInfo || null == resolveInfo.serviceInfo) {
return method.invoke(this.mActivityManager, args); //3、
}
return startDelegateServiceForTarget(target, resolveInfo.serviceInfo, null, RemoteService.EXTRA_COMMAND_START_SERVICE);//4、处理
}
protected ComponentName startDelegateServiceForTarget(Intent target, ServiceInfo serviceInfo, Bundle extras, int command) {
Intent wrapperIntent = wrapperTargetIntent(target, serviceInfo, extras, command); //5、构造代理服务的Intent
return mPluginManager.getHostContext().startService(wrapperIntent); //6、
}
在ActivityManagerProxy的invoke()中拦截到startService()后调用startService(),在startService()中执行一下逻辑:
- 获取Intent传入的参数,并根据Intent判断是否为插件中服
- 对于host中的Service,直接调用原方法完成启动
- 插件中的Service调用startDelegateServiceForTarget()方法,最终调用wrapperTargetIntent()修改启动的Intent
- 启动代理服务,进入LocalService中 -. wrapperTargetIntent():构造代理服务的Intent
protected Intent wrapperTargetIntent(Intent target, ServiceInfo serviceInfo, Bundle extras, int command) {
target.setComponent(new ComponentName(serviceInfo.packageName, serviceInfo.name));
String pluginLocation = mPluginManager.getLoadedPlugin(target.getComponent()).getLocation();//1、
boolean local = PluginUtil.isLocalService(serviceInfo); // 2、
Class<? extends Service> delegate = local ? LocalService.class : RemoteService.class;//3、
Intent intent = new Intent();
intent.setClass(mPluginManager.getHostContext(), delegate); // 3、
intent.putExtra(RemoteService.EXTRA_TARGET, target);
intent.putExtra(RemoteService.EXTRA_COMMAND, command);
intent.putExtra(RemoteService.EXTRA_PLUGIN_LOCATION, pluginLocation);
return intent;
}
构造代理服务Intent的流程如下:
- 获取插件apk路径
- 判断要启动的插件服务是本地服务还是远程服务,并选择LocalService或RemoteService
- 将Intent的Class设置代理服务,并保存目标插件Service信息
- LocalService:服务启动后代理分发调用插件服务的方法
public int onStartCommand(Intent intent, int flags, int startId) {
Intent target = intent.getParcelableExtra(EXTRA_TARGET); //1、
ComponentName component = target.getComponent();
LoadedPlugin plugin = mPluginManager.getLoadedPlugin(component);
target.setExtrasClassLoader(plugin.getClassLoader());
switch (command) {
case EXTRA_COMMAND_START_SERVICE: {
ActivityThread mainThread = ActivityThread.currentActivityThread();
IApplicationThread appThread = mainThread.getApplicationThread();
Service service;
if (this.mPluginManager.getComponentsHandler().isServiceAvailable(component)) {
service = this.mPluginManager.getComponentsHandler().getService(component); // 2、
} else {
try {
service = (Service) plugin.getClassLoader().loadClass(component.getClassName()).newInstance(); //3、
Application app = plugin.getApplication();
IBinder token = appThread.asBinder();
Method attach = service.getClass().getMethod("attach", Context.class, ActivityThread.class, String.class, IBinder.class, Application.class, Object.class);
IActivityManager am = mPluginManager.getActivityManager();
attach.invoke(service, plugin.getPluginContext(), mainThread, component.getClassName(), token, app, am); //4、
service.onCreate();
this.mPluginManager.getComponentsHandler().rememberService(component, service); //5、
} catch (Throwable t) {
return START_STICKY;
}
}
//6、执行插件Service的onStartCommand()传入参数
service.onStartCommand(target, 0, this.mPluginManager.getComponentsHandler().getServiceCounter(service).getAndIncrement());
break;
}
}
return START_STICKY;
}
LocalService启动后,程序执行到onStartCommand()中,在onStartCommand()会创建插件Service的实例并完成服务的分发:
- 获取真实Service对应的ComponentName 和插件的LoadedPlugin
- 先从缓存中是获取Service对象
- 缓存中没找到的,使用插件的ClassLoader创建插件Service的对象
- 反射获取插件的attach()方法,然后执行插件服务的attach()和onCreate()
- 缓存Service 对象
- 停止服务
- 拦截stopService()后设置代理服务的Intent,停止LocalService
- 在LocalService中代理分发停止对应的服务
case EXTRA_COMMAND_STOP_SERVICE: {
//1、获取Service并移除当前缓存
Service service = this.mPluginManager.getComponentsHandler().forgetService(component);
if (null != service) {
try {
service.onDestroy(); //2、调用onDestroy()
}
}
break;
}
- 绑定服务
- bindService()
protected Object bindService(Object proxy, Method method, Object[] args) throws Throwable {
Intent target = (Intent) args[2];
ResolveInfo resolveInfo = this.mPluginManager.resolveService(target, 0);
Bundle bundle = new Bundle();
PluginUtil.putBinder(bundle, "sc", (IBinder) args[4]); // 1、保存绑定时传入的ServiceConnection对象
startDelegateServiceForTarget(target, resolveInfo.serviceInfo, bundle, RemoteService.EXTRA_COMMAND_BIND_SERVICE);
mPluginManager.getComponentsHandler().remberIServiceConnection((IBinder) args[4], target); // 2、缓存IServiceConnection
return 1;
}
- 设置代理服务的Intent
- 缓存绑定服务的IServiceConnection
public void remberIServiceConnection(IBinder iServiceConnection, Intent intent) {
synchronized (this.mBoundServices) {
mBoundServices.put(iServiceConnection, intent); //
}
}
- 在LocalService中处理绑定服务,回调onBind()和connected()方法
case EXTRA_COMMAND_BIND_SERVICE: {
ActivityThread mainThread = ActivityThread.currentActivityThread();
IApplicationThread appThread = mainThread.getApplicationThread();
Service service = null;
if (this.mPluginManager.getComponentsHandler().isServiceAvailable(component)) {
service = this.mPluginManager.getComponentsHandler().getService(component);
} else {
try {
service = (Service) plugin.getClassLoader().loadClass(component.getClassName()).newInstance();
......
try {
IBinder binder = service.onBind(target); // 1、回调Service的onBind()
IBinder serviceConnection = PluginUtil.getBinder(intent.getExtras(), "sc");
IServiceConnection iServiceConnection = IServiceConnection.Stub.asInterface(serviceConnection);
if (Build.VERSION.SDK_INT >= 26) {
iServiceConnection.connected(component, binder, false);//2、获取传入的serviceConnection,回调connected()
} else {
Reflector.QuietReflector.with(iServiceConnection).method("connected", ComponentName.class, IBinder.class).call(component, binder);
}
}
break;
}
绑定服务整体过程和启动类似,只是多了两个步骤:
- 创建ServiceConnection后,在mBoundServices中保存Intent和IServiceConnection
- 在LocalService中回调Service的onBind()和 connected()
- 解绑服务
- 从缓存的IServiceConnection中获取启动服务时的Intent
protected Object unbindService(Object proxy, Method method, Object[] args) throws Throwable {
IBinder iServiceConnection = (IBinder) args[0];
Intent target = mPluginManager.getComponentsHandler().forgetIServiceConnection(iServiceConnection);
ResolveInfo resolveInfo = this.mPluginManager.resolveService(target, 0);
startDelegateServiceForTarget(target, resolveInfo.serviceInfo, null, RemoteService.EXTRA_COMMAND_UNBIND_SERVICE);
return true;
}
public Intent forgetIServiceConnection(IBinder iServiceConnection) {
synchronized (this.mBoundServices) {
Intent intent = this.mBoundServices.remove(iServiceConnection);
return intent;
}
}
- 从绑定服务时保存的mBoundServices中获取启动的Intent
- 再LocalService中处理解绑服务
case EXTRA_COMMAND_UNBIND_SERVICE: {
Service service = this.mPluginManager.getComponentsHandler().forgetService(component); // 获取服务
if (null != service) {
try {
service.onUnbind(target); // 解绑服务
service.onDestroy();
}
}
break;
}
5、ContentProvider插件化
- ContentProvider使用
Uri bookUri = Uri.parse("content://com.didi.virtualapk.demo.book.provider/book”); // 查找的Uri
LoadedPlugin plugin = PluginManager.getInstance(this).getLoadedPlugin(pkg); // 1、获取插件的LoadedPlugin
bookUri = PluginContentResolver.wrapperUri(plugin, bookUri); //2、替换Uri,用于启动宿主代理的ContentProvider
//3、获取ContentResolver对象,执行查询
Cursor bookCursor = getContentResolver().query(bookUri, new String[]{"_id", "name"}, null, null, null);
- Host中注册代理的ContentProvider
<provider
android:exported="false"
android:name="com.didi.virtualapk.delegate.RemoteContentProvider"
android:authorities="${applicationId}.VirtualAPK.Provider"
android:process=":daemon" />
- PluginContentResolver.wrapperUri():替换Uri启动宿主代理Provider
public static Uri wrapperUri(LoadedPlugin loadedPlugin, Uri pluginUri) {
String pkg = loadedPlugin.getPackageName(); //1、
String pluginUriString = Uri.encode(pluginUri.toString());
StringBuilder builder = new StringBuilder(RemoteContentProvider.getUri(loadedPlugin.getHostContext()));//2、
//3、
builder.append("/?plugin=" + loadedPlugin.getLocation());
builder.append("&pkg=" + pkg);
builder.append("&uri=" + pluginUriString);
//4、创建Uri
Uri wrapperUri = Uri.parse(builder.toString());
return wrapperUri;
}
public static String getAuthority(Context context) {
return context.getPackageName() + ".VirtualAPK.Provider”; // 创建代理Provider启动权限
}
public static String getUri(Context context) {
return "content://" + getAuthority(context);
}
和代理Service大致相似,Provider的使用也分为两部分,处理启动的Intent和启动Provider的代理分发,Intent处理主要将请求的Uri转换为启动代理Provider的Uri,同时保存插件的请求信息:
- 获取原请求Uri的包名和Uri参数
- 获取代理Provider的启动Uri替换启动插件的Uri,将插件启动变成请求启动host中代理的ContentProvider
- 保存请求插件的路径、插件包名、插件的Uri参数
- 创建并返回新的Uri
- getContentResolver().query()
@Override
public ContentResolver getContentResolver() {
return new PluginContentResolver(getHostContext());
}
由前面的PluginContext知道,在插件的四大组件中使用PluginContext代替Context。对于插件此处调用的是PluginContext中的getContentResolver(),创建PluginContentResolver对象
- PluginContentResolver
public class PluginContentResolver extends ContentResolverWrapper {
private PluginManager mPluginManager;
public PluginContentResolver(Context context) {
super(context);
mPluginManager = PluginManager.getInstance(context);
}
@Override
protected IContentProvider acquireProvider(Context context, String auth) {
//2、处理插件中的Provider
if (mPluginManager.resolveContentProvider(auth, 0) != null) {
return mPluginManager.getIContentProvider();
}
return super.acquireProvider(context, auth);
}
}
PluginContentResolver继承ContentResolverWrapper类,作为插件中使用的ContentProvider,由Provider的工作过程知道(ContentProvider使用和工作过程详解),系统在获取Provider对象时会调用getIContentProvider()
- getIContentProvider():返回创建的Provider对象,此处使用宿主的代理的Provider
public synchronized IContentProvider getIContentProvider() {
if (mIContentProvider == null) {
hookIContentProviderAsNeeded(); //Hook ContentProvider 拦截请求
}
return mIContentProvider;
}
- hookIContentProviderAsNeeded():Hook宿主中代理的Provider
protected void hookIContentProviderAsNeeded() {
Uri uri = Uri.parse(RemoteContentProvider.getUri(mContext));
mContext.getContentResolver().call(uri, "wakeup", null, null);
try {
Field authority = null;
Field provider = null;
ActivityThread activityThread = ActivityThread.currentActivityThread();
//1、
Map providerMap = Reflector.with(activityThread).field("mProviderMap").get();
Iterator iter = providerMap.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry entry = (Map.Entry) iter.next();
Object key = entry.getKey();
Object val = entry.getValue();
String auth;
if (auth.equals(RemoteContentProvider.getAuthority(mContext))) { //2、
if (provider == null) {
provider = val.getClass().getDeclaredField("mProvider");
provider.setAccessible(true);
}
IContentProvider rawProvider = (IContentProvider) provider.get(val);
//3、代理指定的Provider
IContentProvider proxy = IContentProviderProxy.newInstance(mContext, rawProvider);
mIContentProvider = proxy;
break;
}
}
}
}
Hook ContentProvider的过程:
- 反射获取ActivityThread中保存的ContentProvider集合
- 遍历mProviderMap,查找Host中的代理Provider,此时我们知道查询的Uri已经替换为启动代理Provider的Uri
- 然后动态代理Host中的Provider
- RemoteContentProvider:宿主中代理Provider
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
ContentProvider provider = getContentProvider(uri); // 1、获取真正执行的Provider对象
Uri pluginUri = Uri.parse(uri.getQueryParameter(KEY_URI)); //2、获取启动Plugin真正的Uri
if (provider != null) {
return provider.query(pluginUri, projection, selection, selectionArgs, sortOrder);//3、
}
return null;
}
在宿主代理Provider中:
- 首先根据请求的Uri获取插件的Provider,
- 然后获取启动Plugin真正的Uri
- 执行provider中方法
- . getContentProvider():创建插件中Provider对象,并调用Provider中方法
private ContentProvider getContentProvider(final Uri uri) {
final PluginManager pluginManager = PluginManager.getInstance(getContext());
Uri pluginUri = Uri.parse(uri.getQueryParameter(KEY_URI)); // 1、
final String auth = pluginUri.getAuthority(); // 2、
ContentProvider cachedProvider = sCachedProviders.get(auth); // 3、
if (cachedProvider != null) {
return cachedProvider;
}
synchronized (sCachedProviders) {
LoadedPlugin plugin = pluginManager.getLoadedPlugin(uri.getQueryParameter(KEY_PKG)); //4、
if (plugin == null) {
try {
pluginManager.loadPlugin(new File(uri.getQueryParameter(KEY_PLUGIN))); // 5、
}
}
final ProviderInfo providerInfo = pluginManager.resolveContentProvider(auth, 0); // 6、
if (providerInfo != null) {
RunUtil.runOnUiThread(new Runnable() {
@Override
public void run() {
try {
LoadedPlugin loadedPlugin = pluginManager.getLoadedPlugin(uri.getQueryParameter(KEY_PKG)); // 7、
ContentProvider contentProvider = (ContentProvider) Class.forName(providerInfo.name).newInstance(); // 8、
contentProvider.attachInfo(loadedPlugin.getPluginContext(), providerInfo); // 9、
sCachedProviders.put(auth, contentProvider); //10、
}
}
}, true);
return sCachedProviders.get(auth);
}
}
return null;
}
前面的整个过程都是在操纵代理Provider,真正牵涉到插件的Provider就在此处,整个创建Provider和执行方法流程如下:
- 从请求的Uri中获取原来请求插件的Uri
- 获取请求插件Provider的权限
- 首先从缓存中获取对应的Provider对象
- 从Uri中插件获取包名,再根据包名获取对应的LoadedPlugin
- 如果程序未加载过此插件,则执行加载插件
- 根据请求的权限,遍历解析时缓存的provider集合查找 ProviderInfo 对象
- 从请求的Uri中获取插件的包名,根据包名获取创建的LoadedPlugin对象
- 使用ClassLoader创建Provider对象
- 调用Provider.attachInfo()完成context的绑定
- 缓存创建的Provider对象
到此整个VirtualApk的使用和源码分析就到此结束了,从开始接触到现在源码分析整个过程拖了好久,通过整个流程的学习对之前的源码知识也有了更好的巩固,希望对想学习插件化的同学有所帮助;