美文网首页
DroidPlugin初探

DroidPlugin初探

作者: Youremywoman | 来源:发表于2019-04-30 11:02 被阅读0次

第三方美小二加载直连流程

美小二插件使用的是DroidPlugin框架 所以先得理解一下这个框架的原理

DroidPlugin Git地址

参考资料


  • 如何把插件apk中的代码和资源加载到当前虚拟机
  • 如何把插件apk中的四大组件注册到进程中。
  • 如何防止插件apk中的资源和宿主apk中的资源引用冲突
初始化插件环境

入口BizApplication onCreate方法中调用 PluginHelper.getInstance().applicationOnCreate(getBaseContext());

  • 进入HookFactory的installHook方法。这里先走第一次是宿主进程的流程 安装IActivityManagerHook和IPackageManagerHook 在onInstall方法利用动态代理对AMS和PMS的端代理进行hook。具体处理类参考IActivityManagerHookHandle,IPackageManagerHookHandle。

  • 进入PluginManager的connectToService 进入PluginServiceProvider的call方法。初始化IPluginManagerImpl该类提供对插件的管理服务。调用loadAllPlugin

    • 进入MyActivityManagerService的onCreate方法该类功能
      1、系统预定义N个进程。每个进程下有4中launchmod的activity,1个服务,一个ContentProvider。
      2、每个插件可以在多个进程中运行,这由插件自己的processName属性决定。
      3、插件系统最多可以同时运行N个进程,M个插件(M <= N or M >= N)。
      4、多个插件运行在同一个进程中,如果他们的签名相同。(我们可以通过一个开关来决定。)
      5、在运行第M+1个插件时,如果预定义的N个进程被占满,最低优先级的进程会被kill掉。腾出预定义的进程用来运行此个插件

      StaticProcessList:该类主要缓存宿主包下四大组件的信息 key=processName value=ProcessItem ProceeItem中 activityInfos key=ActivityInfo.name,value=ActivityInfo,serviceInfos key=ServiceInfo.name,value=ServiceInfo。

      RunningProcessList: 正在运行的进程列表set当前context

    loadHostRequestedPermission:缓存预置请求权限,mLock.notifyAll() 解除waitForReadyInner

    进入PluginManager的onServiceConnected方法 。IActivityManagerHook和IPackageManagerHook开启hook

安装插件包

PluginManager.installPackage(String filepath, int flags) 主要功能

PackageInfo info = pm.getPackageArchiveInfo(filepath, 0); 获取插件apk包信息

PluginDirHelper.getPluginApkFile(mContext, info.packageName);安装路径data/data/{host-packagename}/Plugin/{plugin-packagename}

PluginPackageParser 利用系统的PackageParser将插件AndroidMainfest解析保存到内存中
(这里各个版本的兼容在com.morgoo.droidplugin.pm.parser包下)

启动插件包Activity

找到美小二LoadPluginActivity startPlugin 打个断点。

进入IActivityManagerHookHandle.startActivity的beforeInvoke 方法

这里面就有文章了看看开启前如何将目标Activity替换成AndroidManifest中的Activity

1. 找到startActivity方法中的intent参数
2.  IPluginManagerImpl.getActivityInfo 通过intent中的className找到插件中对应的Activity(安装插件包时有将activity信息缓存在PackageParser中),通过PackageParser的generateActivityInfo生成对应的ActivityInfo
3. 选择代理的Activity(Android有一个限制:**必须在AndroidManifest.xml中显示声明使用的Activity**),所以这里要选择一个在AndroidManifest中预先埋好的Activity
4. IPluginManagerImpl.selectStubActivityInfoByIntent 先从MyActivityManagerService的mRunningProcessList 找到是否有正在运行的进程,如果有则直接使用之,否则从mStaticProcessList中选择(mStaticProcessList中的数据在初始化插件环境的时候填充里面是宿主AndroidManifest的信息)合适的ActivityInfo(比较launchMode)。
5. 构建宿主ProxyAcitivity的intent 设置回宿主的Classloader(在插件中使用的是插件自定义classLoader要区分开)。将原来的intent当做bundle传递。

上面是将Activity替换成ProxyActivty欺骗系统的过程,下面分析AMS启动Activity时如何替换成原来的Activity.

这边在新进程第一次启动Activity重新走到宿主Application的onCreate方法。开始初始化插件进程的一些环境

  1. hook各种Binder服务。插件访问服务使用宿主的配置 (HookFactory的installHook)

  2. PluginCallbackHook的onInstall 中讲ActivityThread中的mH的mCallback替换为PluginCallback

在Activity的期待流程中可以知道AMS startActivity后会交给ActivityThread启动,ActivityThread通过

Handler mH去分发事件

  1. 在PluginCallback的handleLaunchActivity方法中打个断点.获取通过intent传递过来的原始targetIntent,获取

    我们实际要启动的ComponentName 通过插件的PackageParser获取到ActivityInfo

  2. PluginProcessManager.preLoadApk(mHostContext, targetActivityInfo);

这个方法也比较重要 做了下面几个事

  • 通过插件的applicationInfo创建了一个loadedApk
  • 创建了一个com.morgoo.droidplugin.core.PluginClassLoader 用于加载插件apk中的类hook到loadApk上,当前线程Thread.currentThread().setContextClassLoader(classloader);
  • 如果loadedApk的mApplication为空。执行makeApplication方法 这时候就会走到PluginInstrumentation的callApplicationOnCreate 走到插件apk的AndroidManifest中定义的Application类的onCreate方法
  • 将targetIntent 写入msg.obj的intent字段
  1. 我们知道Activity的创建在ActivityThread的performLaunchActivity方法中。

    activity = mInstrumentation.newActivity(
            cl, component.getClassName(), r.intent);
    

    这里的cl 被我们hook了就是.PluginClassLoader compenet是从targetIntent获取的 所以这里反射获取的是我们插件中的Activity 到这一步Activity 就替换回来了。AMS也接管了该Activity的生命周

资源问题

由于加载插件新开了个进程 第一次LoadedApk.getResources。插件中loadapk被我们hook 里面的资源resDir也是插件包路径.AssetManager的addPath也是add的resDir 所以资源是分离的。如果要宿主访问插件的资源可以使用createPackageContext方式访问

插件版本管理

我们的美小二中的插件版本管理逻辑在com.mwee.android.mwpluginupgrade.load.LoadPluginPresenter中

简单的对插件化做了下了解 还有很多问题没深入

相关文章

网友评论

      本文标题:DroidPlugin初探

      本文链接:https://www.haomeiwen.com/subject/bjumnqtx.html