RePlugin分析

一、插件安装流程

简单流程图


1.插件的安装入口是 RePlugin.install 方法,这个方法核心的实现是在PluginManagerServer.java 的installLocked
方法
2. installLocked 方法里处理 
  1> getPackageArchiveInfo  PackageManager.GET_META_DATA | PackageManager.GET_SIGNATURES 
  2> verifySignature 验证签名
  3> parseFromPackageInfo 获取metaData信息
  4> copyOrMoveApk 文件
  5> uzip copy so文件
  6> 保存插件信息

二、android  四大组件在插件中处理流程

1.activity 处理流程

  宿主启动插件activity方式  RePlugin.startActivity


1> getActivityInfo 获取activityInfo

流程图


    

1.首先检查插件是否已经初始化如果么有,就开始出初始化插件 创建Loader对象,调用loader中的LoadDex 来获取Dex中信息(四大组件信息)创建ComponentListResources,PluginDexClassLoader,PluginContext,等等,
    

2. loadEntryLocked 查找插件中Entry类,并调用Create方法,在create方法中,会初始化插件信息,为插件调用host方法做准备
    

3. callApp 检查并初始化Application 类,传递mPkgContext

4.获取activityInfo对象
 


2> PluginClientHelper.getProcessInt  获取或者启动进程

流程图



1.根据插件processName获取进程标示
    

2.调用到PmBase.startPluginProcessLocked  检查插件是否已经初始化完成
    

3.分配进程编号PluginProcessMain.allocProcess 查找空闲的进程编号
    

4. PluginProviderStub.proxyStartPluginProcess启动进程,这里启动进程的方式很巧妙 (首先Mainifest中配置不同进程的Provider,然后根据进程编号调用不同的Provider当provider启动后它的进程也就启动啦)

5.当进程启动的时候,就会调用RePluginApplication 在这里会初始化进程信息,并通过 PluginProcessMain.connectToHostSvc();方法和host建立连接,并传递给host自己的client binder对象

6.此时可以获取到client binder对象,



3> client.allocActivityContainer 分配坑位

   流程图


1. PluginContainers.alloc 根据activity的【是否透明,taskAffinity,launchMode,process】获取匹配一个已经在Mainifest配置好的Activity
   

2. 设置已经分配好的坑位信息,保存插件名称,真实activity信息,设置坑位已经使用状态



4>将坑位更为会真实的activity

  流程图

  


1.当调用context.startActivity 后,AMS最后会在ActivityThread中调用classLoader.loadClass 由于classLoader 已经替换成RePluginClassLoader

2. 在 RePluginClassLoader 的loadClass 调用的是PMF.loadClass 一步一步调用到 PluginProcessPerresolveActivityClass方法
  

3. 在resolveActivityClass方法中 根据坑位查找到对应的真实activity 和插件信息,通过插件信息中的classloader加载真实的activity 达到偷梁换柱



5>activity启动起来的时候需要替换contex将坑位更为会真实的activity 

1 在插件工程编译的时候gradle已经将activity的父类替换成对应的ReplugActivityxx 在这些activity中的attachBaseContext 方法中替换掉Context

2.通过newBase = RePluginInternal.createActivityContext(this, newBase);方法最终调用到Loader.java 里createBaseContext方法 new PluginContext 这样就替换掉Context

3.以后在activity中对context的操作都会调用PluginContext 方法


6> activity 销毁

1 在插件工程编译的时候gradle已经将activity的父类替换成对应的ReplugActivityxx 在这些activity中的复写了onDestroy 
  

2 在onDestory中调用RePluginInternal.handleActivityDestroy(this);方法,这个方法最终调用到PluginContainers类
  

3. PluginContainers.getInstance().recycle 回收对于的坑位



2.service 处理流程

流程图


1>startService
   

通过PluginServiceClient. startService 启动service  
   

1.通话intent 在查找到ServiceInfo
  

2.通话PluginClientHelper.getProcessInt 获取到对应进程编号
  

3. 获取对应进程的PluginServiceServer对象 如果不存在启动对应的进程  (MP.startPluginProcess启动进程,在activity中已经讲述了如何启动进程这里就不多说啦)

4. 调用PluginServiceServerstartService
  

5. 从ComponentList 中获取ServiceInfo
   

6. 获取插件信息,用对应的classLoader加载service,然后调用service 中的attachBaseContext方法传递context,设置mApplication,最后调用onCreate方法
   

7. 为了防止service被系统杀掉,这里会启动对应的坑位的service 


2>stopService

stopServic主要实现是在PluginServiceServer里

1.service是否启动
  

2.当前serice是否bind,如果么有
  

3. recycleServiceLocked 回收servie ,调用onDestroy方法,并且停止坑位服务


3>bindService
  

bindService主要实现是在PluginServiceServer里和startService大部分相同 不同的是
  

1.检查是否bind过,如果没有回调用onBind
  

2.然后都有传递过来的对象的connected方法


4>unbindService
 

unbindService主要实现是在PluginServiceServer里基本和stopSercie一样,这里就不多说


3.contentProvider 处理流程

流程图



provider入口在PluginProviderClient类里
核心思想是


1.首先在Mainifet中配置一些坑位,这些坑位配置不同进程



2.通过PluginProviderClient的Uri turi = toCalledUri(c, uri);方法会把uri替换成


// from => content://                                                  com.qihoo360.contacts.abc/people?id=9

// to   => content://com.zcb.plugin.host.Plugin.NP.x /plugin_name/com.qihoo360.contacts.abc/people?id=9

这样在调用c.getContentResolver().query/insert/xxx的时候就会调用到配置的坑位Provider 这些坑位的Provider都继承PluginPitProviderBase


3.在PluginPitProviderBase 类里在截取到真实的uri,更加真实的uri查询到ContentProvider 和插件信息


4.获取对应的classLoder加载ContentProvider 初始化Provider并 调用attachInfo方法传递Context


5.调用对应的方法 query/inster/update/xx



4.broadcastReceiver 处理流程

流程图



核心思想.receiver是在解析dex完成以后注册到host中一个总的receiver中,通过这个总的receiver代理转发
1.loader.loadDex 的时候解析出receiver,并通过regReceivers方法调用host中的 regReceiver方法


2.pmHostSvc regReceiver方法,将filter 通过mContext.registerReceiver(mReceiverProxy, filter);注册


3.当sendBroadcast的时候,mReceiverProxy接收到广播,根据action找到真实receiver以及插件信息


4.获取客户端client binder如果不存在就启动进程MP.startPluginProcess 详细见activity中进程启动方式解读


5.调用客户端的PluginReceiverHelper.onPluginReceiverReceived方法


6.获取classLoader 加载receiver 放入缓存,通过UI线程调用onReceive 方法

三、占坑方式

1.activity 占坑方式
通过在Mainifest中配置不同的activity来供插件中不同类型的activity使用
主要分为【透明与透明,不同的launchMode,不同的taskAffinity,不同的process】
2.service 多进程
配置不同的进程process

3.provider 多进程
配置配置不同的进程process


四、插件与host双向如何通信

主要方式:

1.宿主启动的时候 applicaton的实现类配置的是RePluginApplication,在应用启动的时候调用attachBaseContext方法


1. attachBaseContext 方法会调用 RePlugin.App.attachBaseContext


2. PMF.init(app)会被调用 然后调用到 PmBase 中的init方法

3.init方法会调用initForServer 会初始化host信息,此时host 守护进程就启动啦



由于插件实际上就是宿主fork的进程,和公用宿主的代码,所以在启动插件进程的时候,也会启动RePluginApplication 类

插件启动的时候 applicaton的实现类配置的是RePluginApplication,在应用启动的时候调用attachBaseContext方法


1. attachBaseContext 方法会调用 RePlugin.App.attachBaseContext


2. PMF.init(app)会被调用 然后调用到 PmBase 中的init方法


3.init方法在不是persistent的时候会调用initForClient 


4. initForClient 调用PluginProcessMain.connectToHostSvc(); 


5.通过Provider 获取到hostBinder对象,然后和host 建立连接


6.再调用PMF.sPluginMgr.attach(); 把客户端clientBinder 注册到host里,

通过以上步骤,host里有clientBinder对象,插件里有hostBinder对象,两者就可以通信


其他方式:
宿主可以注册一个服务 RePlugin.registerHostBinder 给其他插件调用
插件还可以通过 RePlugin.registerPluginBinder 方法注册服务给宿主调用 ,可以调用方式 RePlugin.fetchBinder获取服务


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值