android:
theme="@style/Theme.AppCompat"android:
name="com.qihoo360.replugin.sample.host.loader.a.ActivityN1STTS0"
android:
exported="false"
android:
screenOrientation="portrait"android:
configChanges="keyboard|keyboardHidden|orientation|screenSize"/>
2.2、replugin-host-library:
对应com.qihoo360.replugin:
replugin-host-lib:
xxx依赖,
是一个Java工程,由主程序负责引入,是RePlugin的核心工程,负责初始化、加载、启动、管理插件等。
2.3、replugin-plugin-gradle:
对应com.qihoo360.replugin:
replugin-plugin-gradle:
xxx
是一个Gradle插件,由插件负责引入,主要负责在插件的编译期中:
配置插件打包相关信息;动态替换插件工程中的继承基类,如下,修改Activity的继承、Provider的重定向等。
/*LoaderActivity替换规则*/defprivatestaticloaderActivityRules=[
'android.app.Activity'
'com.qihoo360.replugin.loader.a.PluginActivity',
'android.app.TabActivity'
'com.qihoo360.replugin.loader.a.PluginTabActivity',
'android.app.ListActivity'
'com.qihoo360.replugin.loader.a.PluginListActivity',
'android.app.ActivityGroup'
'com.qihoo360.replugin.loader.a.PluginActivityGroup',
'android.support.v4.app.FragmentActivity':
'com.qihoo360.replugin.loader.a.PluginFragmentActivity',
'android.support.v7.app.AppCompatActivity':
'com.qihoo360.replugin.loader.a.PluginAppCompatActivity',
'android.preference.PreferenceActivity'
'com.qihoo360.replugin.loader.a.PluginPreferenceActivity',
'android.app.ExpandableListActivity'
'com.qihoo360.replugin.loader.a.PluginExpandableListActivity'
2.4、replugin-plugin-library:
对应com.qihoo360.replugin:
replugin-plugin-lib:
xxx依赖,
是一个Java工程,由插件端负责引入,主要提供通过“Java
反射”来调用主程序中
RePluginHostLibrary的相关接口,
PluginServiceClient
中的RePlugin、
其中的RePlugin、RePluginInternal、
都是反射宿主App:
replugin-host-library
RePluginInternal、PluginServiceClient类方法。
四、Replugin的ClassLoader。
这里主要介绍,宿主和插件使用的ClassLoader,以及它
们的创建和Hook住时机。
这是RePlugin唯一的Hook点,
而其中插件ClassLoader和宿主ClassLoader是相互关系的,如下图。
将就的图
1、宿主的ClassLoader
RePluginClassLoader,宿主的ClassLoader,继承
PathClassLoader,构造方法使用原ClassLoader,和原
ParentLoader是因为双亲代
ClassLoader的Parent生成。
其中
用于欺骗系统还处于原Loader,并且从原Loader中反射出常
用方法,用于重载方法中使用。
拷贝资源方式方法
宿主Loader
中,主要是重载了loadClass,其中从PMF
(RePlugin中公开接口类)中查找class,如果存在即返回插
件class,如果不存在就从原Loader中加载。
从而实现了对
加载类的拦截。
这里的PMF在加载class时,其实用的是下面【2、插
件的ClassLoader】:
PluginDexClassLoader,这个后面流程会讲到。
2、插件的ClassLoader
PluginDexClassLoader,继承DexClassLoader,构造时持
有了宿主的ClassLoader,从宿主ClassLoader中反射获取
loadClass方法,当自己的loadClass方法找不到类时,从宿
创建:
上面1、2中两个Loader,是宿主在初始化时创
建的,初始化时可以选择配置RePluginCallbacks,callback
中提供方法默认创建Loader,你也可以实现自定义的
ClassLoader,但是需要继承以上的Loader,如下图。
//初始化方式创建
RePlugin.getConfig().getCallbacks().createClassLoader(oClassLoader.getParent(),oClassLoader);RePluginCallbacks
Hook:
初始化时,PatchClassLoaderUtils会在Application
的attachBaseContextO中,通过patch(application)Hook住宿主的ClassLoader,patch内部如下图。
hookClassLoader
五、Replugin的相关类介绍
提前介绍一些功能类,后面就不做详细介绍。
1、RePlugin
:
RePlugin的对外入口类,提供install、uninstall、preload、startActivity、fetchPackageInfo、fetchComponentList,
fetchClassLoader等等统一的方法入口,用户操作的主要是它。
2、RePlugin.App:
RePlugin中的内部类,针对Application的入口类,所有针对插件Application的调用应从此类开始和初始化,想象成插件的Application吧。
3、PmBase:
RePlugin常用mPluginMgr变量表示,可以看作插件管理者。
初始化插件、加载插件等一般都是从它开始。
4、PluginContainers:
插件容器管理中心。
5、PmLocalImpl:
各种本地接口实现,如startActivity,
getActivityInfo,loadPluginActivity等。
6、PmInternalImpl:
类似Activity的接口实现,内部实现了真正startActivity的逻辑、还有插件Activity生命周期的接口。
准备好了吗,骚年
六、Replugin的初始化
那就是从Application初始化开始看起,枯燥的流程就
要开始了,忍住兄弟,我们能赢。
首先我们先看下面这流程图,大致了解启动流程:
将就的看吧
1、attachBaseContext
首先是从Application的attachBaseContext初始化开
始。
如下图,这里主要是配置RePluginConfig和
中的RePluginCallbacks提供了
RePluginCallbacks,然后根据Config去初始化插件。
值得注意的是,RePluginConfig
默认方法创建RePlugin的ClassLoader,还记得上面的介绍
吗?
看图看图
2、插件App.attachBaseContext
继续上面的流程,进入
RePlugin.App.attachBaseContext(this,c),如下图,这里主要
是初始化插件相关的进程、配置信息、插件的主框架和接口、根据默认路径、加载默认插件等。
插件的初始化从这里开始,其中主要为PMF.init()和PMF.callAttach()。
继续看图看图
3、主程序接口PMF.init()/PMF.callAttach()
先进入到PMF.init(),如下图,这里主要实例化了
PmBase类,并初始化了它,创建了内部使用的PmLocalImpl和PmInternalImp接口,同时Hook住主程序的
ClassLoader,替换为RePluginClassLoader,所以接下来的流
程,主要是在PmBase。
PMF.init(),看图吧
PmBase,按照项目中的变量名mPluginMgr,可以理解
为插件的管理者,它管理内部直接或间接的,管理着坑位分配、ClassLoader、插件、进程、启动停止页面的接口等,如
F图。
PmBase创建,还是看图
PmBase的初始化,也就是插件的初始化,这里会启动
各类进程,初始化各种默认插件集合,为后续加载做准备。
plugins-builtin.json和"plugins"文件夹下。
PmBase.init()看图看图
接着PMF.callAttach()其实就是PmBase.callAttach(),
如下图这里开始真正加载插件,初始化插件的
PluginDexClassLoader、加载插件、初始化插件环境和接口。
其中在执行p.load()的时候,会通过
Plugind.callAppLocked()创建插件的Application,并初始化。
PMF.callAttach()看图呗
以上是在主APP的初始化,深入PmBase中
Plugin.load()在加载时,会调用PluginDexClassLoader,通过类名加载Entry类,然后反射出create方法,执行插件的初
始化。
其中
Entry位于Plugin-lib库中。
这里初始化就去到
了插件中了,插件中初始化时,会通过反射的到宿主host类的方法。
4、Application的onCreate
这里主要是切换handler到主线程,注册各种广播接收
监听,如增加插件、卸载插件、更新插件,可以看出这里设计很多内部进程通信的。
七、Replugin启动Activity
版的,实际代码逻辑复杂多了,但是万变不离其宗,这里帮你梳理流程,描述一些关键的点,让你快速理解Activity的启动流程。
再将就下吧,看图
1、startActivity
RePlugin.startActivity开始,startActivity经历了Factory、
PmLocalImpl,其实大部分启动的逻辑其实主要在
PmInternalImpl中。
具体流程如下图,这里简化了实际代码,关键在于
loadPluginActivity。
这里获取了插件对应的坑位,然后保存了目标Activity的信息,通过系统启动坑位。
因为已经Hook住了ClassLoader,在loadClass时再加
载出目标Activity,这样坑位中承载的,便是绕过系统打开的目标Activity。
下面我们进入loadPluginActivity。
说了看
2、loadPluginActivity
loadPluginActivity其实是PmBase中的PmLocalImpl
内部方法。
如下图,这里主要是根据获取到ActivityInfo,然
后根据坑位去为目标Activity分配坑位。
其中
getActivityInfo是通过插件名称,获得插件对象
Plugin,Plugin可能是初始化中已加载的,如果未加载就加载返回,然后根据Plugin中缓存的坑位信息,返回
ActivityInfo。
下面进入allocActivityContainer看坑位的分配,只有分
配到坑位,插件的Activity才可以启动,这是一个IPC过程。
看图没?
2、allocActivityContainer
allocActivityContainer在类PluginProcessPer
中,还记
得我们在PmBase.init()时初始化过它么?
分配坑位也是
RePlugin的核心之一。
在allocActivityContainer中
主要逻辑是
bindActivity,如下图,bindActivity去找到目标Activity匹配的容器,然后加载目标Activity判断是否存在,并建立映
射,返回容器。
然后分配的逻辑,在PluginContainers.alloc
中。
看我大图
3、PluginContainers.alloc
alloc/alloc2方法分配坑位,最后都是到了allocLocked
如果存在未启动的坑位,就使用它。
如果没有就找最老的:
已经被释放的、或者时间最老的。
如果还不行,那么挤掉最老的一个。
看图说话
4、PulginActivity
上面的流程总结,是替换目标Activity,加载插件,分
配坑位,启动目标坑位,拦截ClassLoader的loadClass去加载返回目标Activity。
这个时候启动的Activity还不完整,从模块框架中我们
知道,在编译时,RePlugin会把继承的Activity替换为如
PluginActivity(当前还有AppComPluginActivity等)。
这时候加载启动的目标Activity,其实是继承了PluginActivity。
中的一些方法,
如下图,PluginActivity重载Activity
实现了Activity的补全和自定义操作,如坑位管理,启动宿头晕目眩了没?
为了实现OneHook这个信念,RePlugin实现了复杂的流程,从代码中可以看出,这些年作者们从中走的的各种坑、各种妥协与坚持、复杂的技术积累、已经经历了多年的严酷考验。
不知道有多少人能完整看到这,码字不易,如有疏漏还
是多多包涵,由于篇(tou)幅(Ian)原因,关于Service等
的就不多做叙述了,不知道本文对你是否能有些帮助,欢迎留言讨论。
最后说“一”句
为什么要去了解一个库实现原理呢?
学习框架的架构
思想?
这是一个原因。
但是归根结底,是帮助你在使用库的
中,能靠自己解决各种问题。
程序员的日常一般都忙于
各种工作,各种技术群中的大佬们,大部分时候,没办法
解答你的各种咨询,所以使用它、了解它、多尝试靠自己去探索突破吧。
其他推荐
RePIuginGithub官方库
RePIugin源码解析之repIugin-host-gradIe(宿主gradIe插件)
@osan
@osan
RePlugin源码解析之replugin-plugin-gradle(插件的gradle插件)
ToMyGithub注意到了吗?
最后的总是我!
Android程序员的日常