当前位置: 首页 > 工具软件 > VirtualAPK > 使用案例 >

Android插件化——VirtualAPK接入与源码分析

家浩瀚
2023-12-01

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项目中所有的四大组件的使用都和原生使用方法一致
  • 注意问题
  1. 要先构建一次宿主app,才可以构建plugin,否则异常
  2. 插件布局文件中要设置资源的ID,否则异常:Cannot get property ‘id’ on null object
  3. plugin 增加 gradle.properties 文件并配置android.useDexArchive=false,否则异常
1.4、在宿主程序中使用插件Plugin
  1. 在宿主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;
}
  1. 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()执行逻辑:

  1. 从宿主Context中解析APk中的metaData
  2. 如果metaData为null,则直接使用context创建PluginManager
  3. 从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()执行流程:

  1. 根据context类型,分别进行获取并赋值mApplication & mContext
  2. 创建ComponentsHandler对象,ComponentsHandler的作用是作为工具类,用于处理Intent和Service服务,关于ComponentsHandler后面会再提到
  3. 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

执行流程:

  1. 从ActivityThread中获取的系统中的Instrumentation对象
  2. 创建代理的Instrumentation对象,内部保存原对象
  3. 反射设置代理的VAInstrumentation对象到ActivityThread中
  4. 同样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 技术 文章中已经接收过程了,这里简单介绍逻辑:

  1. 根据Android版本的处理方式不同 ,8.0 以上获取IActivityManagerSingleton,采用AIDL获取AMS,8.0 之前获取gDefault,使用代理执行AMS
  2. 反射获取系统中原类的IActivityManager的代理类;
  3. 动态代理IActivityManager接口,此处创建的是ActivityManagerProxy对象;
  4. 反射设置代理的IActivityManager实例到系统中;
  5. 获取设置的代理的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()中主要完成两件事:

  1. 根据传入的apk,创建LoadedPlugin对象
  2. 在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,并对应处理返回,具体如下:

  1. PluginPackageManager中保存宿主的PackageManager对象
  2. 在查找信息时根据包名获取缓存的LoadedPlugin对象
  3. 对于获取插件中的信息,返回插件的PackageInfo对象
  4. 如果根据包名获取的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);
}
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中,执行流程:

  1. 根据COMBINE_RESOURCES判断是否合并插件资源,这里的COMBINE_RESOURCES就是在插件项目中配置的applyHostMapping,对于插件中需要使用宿主资源的需要合并;
  2. 调用ResourcesManager.createResources()创建合并资源的Resource
  3. 对插件创建单独的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;
}
  1. 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的创建流程:

  1. 分别获取Plugin apk和host apk中的资源路径
  2. 获取当前项目中的splitSourceDirs,splitSourceDirs是保存当前程序所有已经加载的资源路径,按索引顺序获取多个拆分apk的完整路径,并检查是否已经包含插件的路径,对于未添加的将apk路径添加进去
  3. 反射给PackageInfo设置合并之后的splitSourceDirs,此时表示将插件资源路径添加进去了
  4. 获取ResourceImpl及其配置的映射
  5. 获取添加插件资源路径后的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对象,具体细节:

  1. 反射获取Assetmanager的addAssetPath方法
  2. 对于Android 5.0之前,将host中的资源路径保存在新创建的AssetManager中 ,5.0以后直接从context中获取AssetManager对象
  3. 将Plugin中的资源路径添加到AssetManager对象
  4. 遍历所有插件,将所有资源添加到同一个AssetManager对象
  5. 适配不同的手机类型,创建新的Resource对象
  6. 将所有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

  1. 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);
}
  1. 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插件化加载

  1. 实现原理:根据四大组件之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;
}
  1. 调用mPluginManager.resolveActivity()遍历查找处理Intent的插件,从而获取插件中对应Activity的信息
  2. 使用查询到的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()中:

  1. 获取Intent中设置启动Activity的包名和类
  2. 在Intent中保存真实启动Activity的包名和类名
  3. 调用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:

  1. 获取启动插件包对应的LoadedPlugin对象
  2. 获取对应Activity的ActivityInfo信息
  3. 获取Activity的启动模式和主题
  4. 根据启动模式和主题查找占位Activity对象
  5. 在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重写此方法:

  1. 调用cl.loadClass(className)查找启动的Activity,如果抛出异常即表示启动的是插件Activity
  2. 从启动意图Intent中获取目标Activity的类名,并从PluginManager中获取插件对应的LoadedPlugin对象
  3. 利用Plugin中创建的ClassLoader创建出Activity的实例
  4. 替换插件Activity的mResources对象为Plugin中创建的Resource,从而改变Context资源和类的获取

4、Service 插件化加载

  1. 插件原理:通过Hook系统服务代理IActivityManager,从而拦截到启动和停止服务、解除或绑定服务等操作,拦截操作后启动插件中设置的代理Service,通过本地代理服务避免了插件服务的清单文件注册检查,在启动的Service中代理分发插件Service,从而实现调用插件Service中的方法;
  2. 关于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()方法中干涉启动过程;

  • 启动服务
  1. 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()中执行一下逻辑:

  1. 获取Intent传入的参数,并根据Intent判断是否为插件中服
  2. 对于host中的Service,直接调用原方法完成启动
  3. 插件中的Service调用startDelegateServiceForTarget()方法,最终调用wrapperTargetIntent()修改启动的Intent
  4. 启动代理服务,进入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的流程如下:

  1. 获取插件apk路径
  2. 判断要启动的插件服务是本地服务还是远程服务,并选择LocalService或RemoteService
  3. 将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的实例并完成服务的分发:

  1. 获取真实Service对应的ComponentName 和插件的LoadedPlugin
  2. 先从缓存中是获取Service对象
  3. 缓存中没找到的,使用插件的ClassLoader创建插件Service的对象
  4. 反射获取插件的attach()方法,然后执行插件服务的attach()和onCreate()
  5. 缓存Service 对象
  • 停止服务
  1. 拦截stopService()后设置代理服务的Intent,停止LocalService
  2. 在LocalService中代理分发停止对应的服务
case EXTRA_COMMAND_STOP_SERVICE: {
    //1、获取Service并移除当前缓存
    Service service = this.mPluginManager.getComponentsHandler().forgetService(component);
    if (null != service) {
        try {
            service.onDestroy(); //2、调用onDestroy()
        }
    } 
    break;
}
  • 绑定服务
  1. 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;
}
  1. 设置代理服务的Intent
  2. 缓存绑定服务的IServiceConnection
public void remberIServiceConnection(IBinder iServiceConnection, Intent intent) {
   synchronized (this.mBoundServices) {
      mBoundServices.put(iServiceConnection, intent); // 
   }
}
  1. 在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;
}

绑定服务整体过程和启动类似,只是多了两个步骤:

  1. 创建ServiceConnection后,在mBoundServices中保存Intent和IServiceConnection
  2. 在LocalService中回调Service的onBind()和 connected()
  • 解绑服务
  1. 从缓存的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;
   }
}
  1. 从绑定服务时保存的mBoundServices中获取启动的Intent
  2. 再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,同时保存插件的请求信息:

  1. 获取原请求Uri的包名和Uri参数
  2. 获取代理Provider的启动Uri替换启动插件的Uri,将插件启动变成请求启动host中代理的ContentProvider
  3. 保存请求插件的路径、插件包名、插件的Uri参数
  4. 创建并返回新的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()

  1. getIContentProvider():返回创建的Provider对象,此处使用宿主的代理的Provider
public synchronized IContentProvider getIContentProvider() {
   if (mIContentProvider == null) {
      hookIContentProviderAsNeeded();  //Hook ContentProvider 拦截请求
   }
   return mIContentProvider;
}
  1. 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的过程:

  1. 反射获取ActivityThread中保存的ContentProvider集合
  2. 遍历mProviderMap,查找Host中的代理Provider,此时我们知道查询的Uri已经替换为启动代理Provider的Uri
  3. 然后动态代理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中:

  1. 首先根据请求的Uri获取插件的Provider,
  2. 然后获取启动Plugin真正的Uri
  3. 执行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和执行方法流程如下:

  1. 从请求的Uri中获取原来请求插件的Uri
  2. 获取请求插件Provider的权限
  3. 首先从缓存中获取对应的Provider对象
  4. 从Uri中插件获取包名,再根据包名获取对应的LoadedPlugin
  5. 如果程序未加载过此插件,则执行加载插件
  6. 根据请求的权限,遍历解析时缓存的provider集合查找 ProviderInfo 对象
  7. 从请求的Uri中获取插件的包名,根据包名获取创建的LoadedPlugin对象
  8. 使用ClassLoader创建Provider对象
  9. 调用Provider.attachInfo()完成context的绑定
  10. 缓存创建的Provider对象

到此整个VirtualApk的使用和源码分析就到此结束了,从开始接触到现在源码分析整个过程拖了好久,通过整个流程的学习对之前的源码知识也有了更好的巩固,希望对想学习插件化的同学有所帮助;

 类似资料: