文章目录
- 一、RePlugin职责
- 二、初始化总线
- 三、初始化(常驻进程与非常驻进程共同点)
- 四、常驻进程初始化
- 五、非常驻进程初始化
- 5.1 PmBase.initForClient
- 5.2 PluginProcessMain.connectToHostSvc
- 5.3 PMP.connectToServer获取Server端PMP.mStub
- 5.4 PMF.sPluginMgr.attach Client与Server进行绑定
- 5.5 PmHostSvc.attachPluginProcess `常驻进程`
- 5.6 PluginProcessMain.attachProcess `常驻进程`
- 5.7 PmBase.refreshPluginsFromHostSvc 获取插件列表
- 5.8 PmHostSvc.listPlugins`常驻进程`
- 六、Client进程HOOK流程
- 进程分配
一、RePlugin职责
Replugin共4个库工程, 其中有两个是Gradle插件, 两个java工程lib.
1. replugin-host-gradle
2. replugin-host-library
3. replugin-plugin-gradle
4. replugin-plugin-library
1.1 replugin-host-gradle
主程序使用的Gradle插件, 主要职责是在我们的主程序打包的过程中动态的修改AndroidManifest.xml的信息, 动态的生成各种占位Activity、provider和service的声明.
1.2 replugin-host-library
这个库是由主程序依赖的, 也是Replugin的核心, 它的主要职责是初始化Replugin的整体框架, 整体框架使用了Binder机制来实现多进程直接的沟通和数据共享, 或者说是插件和宿主之间沟通和数据共享, hook住了ClassLoader, 加载插件、启动插件、多插件的管理全部都与这个库辅助.
1.3 replugin-plugin-gradle
这个是插件工程使用的Gradle的插件, 这个库使用了Transform API和Javassist实现了编译期间动态的修改字节码文件, 主要是替换插件工程中的Activity的继承, 全部替换成Replugin库中定义的XXXActivity. 动态的将插件apk中调用LocalBroadcastManager的地方修改为Replugin中的PluginLocalBroadcastManager调用, 动态修改ContentResolver和ContentProviderClient的调用修改成Replugin调用, 动态的修改插件工程中所有调用Resource.getIdentifier方法的地方, 将第三个参数修改为插件工程的包名
1.4 replugin-plugin-library
插件工程依赖, 这个库主要目的是通过反射的方式来使用主程序中接口和功能, 这个库在出现加载插件apk后会进行初始化.
二、初始化总线
RePlugin初始化分析按以下总线进行, UI进程启动会先唤醒常驻进程, 常驻进程初始化完成之后返回常驻进程的IBinder对象给UI进程, UI进程再继续初始化
2.1 分析大致步骤
三、初始化(常驻进程与非常驻进程共同点)
3.1 RePluginApplication.attachBaseContext
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
//获取宿主创建的RePluginConfig
RePluginConfig c = createConfig();
if (c == null) {
//如果为空Replugin自己创建一个RePluginConfig,
c = new RePluginConfig();
}
//获取宿主中的RePluginCallbacks对象,如果为空后面会自动创建一个默认的
//这个类提供了宿主和插件的ClassLoader
RePluginCallbacks cb = createCallbacks();
if (cb != null) {
//设置给config
c.setCallbacks(cb);
}
//初始化
RePlugin.App.attachBaseContext(this, c);
}
3.2 RePlugin.App.attachBaseContext
public static void attachBaseContext(Application app, RePluginConfig config) {
sConfig = config;
//1.配置插件安装路径
sConfig.initDefaults(app);
//2.进程相关初始化
IPC.init(app);
//3.框架和主程序接口代码
PMF.init(app);
PMF.callAttach();
sAttached = true;
}
3.3 IPC.init
初始化进程相关数据
public static void init(Context context) {
//1.获取进程名
sCurrentProcess = SysUtils.getCurrentProcessName();
//2.获取进程id
sCurrentPid = Process.myPid();
//3.获取包名
sPackageName = context.getApplicationInfo().packageName;
//4.设置最终的常驻进程名, 默认 = packageName + ":GuardService"
if (HostConfigHelper.PERSISTENT_ENABLE) {
String cppn = HostConfigHelper.PERSISTENT_NAME;
if (!TextUtils.isEmpty(cppn)) {
if (cppn.startsWith(":")) {
sPersistentProcessName = sPackageName + cppn;
} else {
sPersistentProcessName = cppn;
}
}
}
//5.是否为主进程
sIsUIProcess = sCurrentProcess.equals(sPackageName);
//6.是否为常驻进程
sIsPersistentProcess = sCurrentProcess.equals(sPersistentProcessName);
}
3.3 PMF.init
public static final void init(Application application) {
//1.通过当前进程的名字判断应该将插件分配到哪个进程中
PluginManager.init(application);
sPluginMgr = new PmBase(application);
//2.根本不同进程执行不同的逻辑
sPluginMgr.init();
Factory.sPluginManager = PMF.getLocal();
Factory2.sPLProxy = PMF.getInternal();
PatchClassLoaderUtils.patch(application);
}
3.4 new PmBase
PmBase(Context context) {
if (PluginManager.sPluginProcessIndex == IPluginManager.PROCESS_UI || PluginManager.isPluginProcess()) {
String suffix;
if (PluginManager.sPluginProcessIndex == IPluginManager.PROCESS_UI) {
suffix = "N1";
} else {
suffix = "" + PluginManager.sPluginProcessIndex;
}
mContainerProviders.add(IPC.getPackageName() + CONTAINER_PROVIDER_PART + suffix);
mContainerServices.add(IPC.getPackageName() + CONTAINER_SERVICE_PART + suffix);
}
// 持有插件管理类
mClient = new PluginProcessPer(context, this, PluginManager.sPluginProcessIndex, mContainerActivities);
mLocal = new PluginCommImpl(context, this);
//
mInternal = new PluginLibraryInternalProxy(this);
}
3.5 PmBase.init
void init() {
RePlugin.getConfig().getCallbacks().initPnPluginOverride();
// (默认)“常驻进程”作为插件管理进程,则常驻进程作为Server,其余进程作为Client
if (IPC.isPersistentProcess()) {
// 初始化“Server”所做工作
initForServer();
} else {
// 连接到Server
initForClient();
}
// 最新快照
PluginTable.initPlugins(mPlugins);
}
四、常驻进程初始化
4.1 PmBase.initForServer
- 1、进程初始化
- 2、插件信息加载
private final void initForServer() {
mHostSvc = new PmHostSvc(mContext, this);
PluginProcessMain.installHost(mHostSvc);
StubProcessManager.schedulePluginProcessLoop(StubProcessManager.CHECK_STAGE1_DELAY);
// [Newest!] 使用全新的RePlugin APK方案
List<PluginInfo> l = PluginManagerProxy.load();
if (l != null) {
// 将"纯APK"插件信息并入总的插件信息表中,方便查询
// 这里有可能会覆盖之前在p-n中加入的信息。本来我们就想这么干,以"纯APK"插件为准
refreshPluginMap(l);
}
}
4.2 new PmHostSvc
PmHostSvc(Context context, PmBase packm) {
mContext = context;
mPluginMgr = packm;
// Service管理类
mServiceMgr = new PluginServiceServer(context);
// 插件管理类
mManager = new PluginManagerServer(context);
}
4.3 PluginProcessMain.installHost
// 常驻进程调用,缓存自己的 IPluginHost
static final void installHost(IPluginHost host) {
// sPluginHostLocal = PmHostSvc
sPluginHostLocal = host;
// 连接到插件化管理器的服务端
// 将PluginManagerProxy与PluginServerManager进行关联
PluginManagerProxy.connectToServer(sPluginHostLocal);
}
4.4 PluginManagerProxy.connectToServer
public static void connectToServer(IPluginHost host) throws RemoteException {
if (sRemote != null) {
return;
}
// sRemote指向PluginManagerServer.mStub
sRemote = host.fetchManagerServer();
}
4.5 PMP.load 加载插件
加载插件信息
public static List<PluginInfo> load() throws RemoteException {
// 常驻进程sRemote指向PluginManagerServer.mStub
// 常驻进程sRemote指向PluginManagerServer.mStub的代理Proxy
return sRemote.load();
}
4.6 PluginManagerServer.Stub.load
调用链:
PluginManagerServer.Stub.load
-> PluginManagerServer.loadLocked
-> PluginInfoList.load
public boolean load(Context context) {
// 1. 读出字符串
final File f = getFile(context);
final String result = FileUtils.readFileToString(f, Charsets.UTF_8);
// 2. 解析出JSON
final JSONArray jArr = new JSONArray(result);
for (int i = 0; i < jArr.length(); i++) {
final JSONObject jo = jArr.optJSONObject(i);
final PluginInfo pi = PluginInfo.createByJO(jo);
if (pi == null) {
continue;
}
// 读取本地插件配置信息
addToMap(pi);
}
return true; // 成功
return false;// 失败
}
4.7 PmBase.refreshPluginMap缓存插件信息
private final void refreshPluginMap(List<PluginInfo> plugins) {
if (plugins == null) {
return;
}
for (PluginInfo info : plugins) {
Plugin plugin = Plugin.build(info);
putPluginObject(info, plugin);
}
}
private void putPluginObject(PluginInfo info, Plugin plugin) {
mPlugins.put(info.getPackageName(), plugin);
if (!TextUtils.isEmpty(info.getAlias())) {
mPlugins.put(info.getAlias(), plugin);
}
}
4.8 PluginTable.initPlugins
static final void initPlugins(Map<String, Plugin> plugins) {
synchronized (PLUGINS) {
for (Plugin plugin : plugins.values()) {
putPluginInfo(plugin.mInfo);
}
}
}
private static void putPluginInfo(PluginInfo info) {
// 同时加入PackageName和Alias(如有)
PLUGINS.put(info.getPackageName(), info);
if (!TextUtils.isEmpty(info.getAlias())) {
// 即便Alias和包名相同也可以再Put一次,反正只是覆盖了相同Value而已
PLUGINS.put(info.getAlias(), info);
}
}
五、非常驻进程初始化
5.1 PmBase.initForClient
private final void initForClient() {
// 1. 先尝试连接, 如果常驻进程没有起来, 则启动常驻进程
PluginProcessMain.connectToHostSvc();
// 2. 然后从常驻进程获取插件列表
refreshPluginsFromHostSvc();
}
5.2 PluginProcessMain.connectToHostSvc
static final void connectToHostSvc() {
Context context = PMF.getApplicationContext();
// 1. binder指向常驻进程PmHostSvc的代理BinderProxy
IBinder binder = PluginProviderStub.proxyFetchHostBinder(context);
// 2.对Server端PmHostSvc进行代理, sPluginHostRemote指向IPluginHost.Stub.Proxy
sPluginHostRemote = IPluginHost.Stub.asInterface(binder);
// 连接到插件化管理器的服务端
// 3.通过Server端PmHostSvc拿到Server端插件管理代理类, 此时PmHostSvc类似于ServiceManager,
// 管理其他Binder
PluginManagerProxy.connectToServer(sPluginHostRemote);
// 将当前进程的"正在运行"列表和常驻做同步
// 若常驻进程重启,则应在启动时发送广播,各存活着的进程调用该方法来同步
PluginManagerProxy.syncRunningPlugins();
// 注册该进程信息到“插件管理进程”中
PMF.sPluginMgr.attach();
}
5.3 PMP.connectToServer获取Server端PMP.mStub
public static void connectToServer(IPluginHost host) {
// 返回Server端PMP.mStub的代理IPluginHost.Stub.Proxy
sRemote = host.fetchManagerServer();
}
5.4 PMF.sPluginMgr.attach Client与Server进行绑定
final void attach() {
mDefaultPluginName = PluginProcessMain.getPluginHost().attachPluginProcess(IPC.getCurrentProcessName(), PluginManager.sPluginProcessIndex, mClient, mDefaultPluginName);
}
5.5 PmHostSvc.attachPluginProcess 常驻进程
@Override
public String attachPluginProcess(String process, int index, IBinder binder, String def) {
int pid = Binder.getCallingPid();
IPluginClient client = null;
// client = IPluginClient.Stub.Proxy
client = IPluginClient.Stub.asInterface(binder);
if (client == null) {
return null;
}
// Client与Server关联
return PluginProcessMain.attachProcess(pid, process, index, binder, client, def, mManager);
}
5.6 PluginProcessMain.attachProcess 常驻进程
Server端为Client分配进程标识
/**
* 常驻进程调用,添加进程信息到进程管理列表
* @return 进程的默认插件名称(非框架内的进程返回null)
*/
static final String attachProcess(int pid, String process, int index, IBinder binder, IPluginClient client, String def, PluginManagerServer pms) {
final String plugin = getDefaultPluginName(pid, index, binder, client, def);
final ProcessClientRecord pr = new ProcessClientRecord(process, plugin, pid, index, binder, client, pms);
pr.binder.linkToDeath(pr, 0);
writeProcessClientLock(new Action<Void>() {
@Override
public Void call() {
// 添加进程信息到进程管理列表
ALL.put(pr.name, pr);
return null;
}
});
return plugin;
}
5.7 PmBase.refreshPluginsFromHostSvc 获取插件列表
private void refreshPluginsFromHostSvc() {
List<PluginInfo> plugins = null;
plugins = PluginProcessMain.getPluginHost().listPlugins();
// 判断是否有需要更新的插件
// FIXME 执行此操作前,判断下当前插件的运行进程,具体可以限制仅允许该插件运行在一个进程且为自身进程中
List<PluginInfo> updatedPlugins = null;
if (isNeedToUpdate(plugins)) {
// 通过Server端更新插件列表
updatedPlugins = PluginManagerProxy.updateAllPlugins();
}
// 更新Client插件信息
if (updatedPlugins != null) {
refreshPluginMap(updatedPlugins);
} else {
refreshPluginMap(plugins);
}
}
5.8 PmHostSvc.listPlugins常驻进程
public List<PluginInfo> listPlugins() throws RemoteException {
return PluginTable.buildPlugins();
}
调用链
PluginTable.buildPlugins()
-> MP.getPlugins(false)
/**
* 获取当前所有插件信息快照。内部框架使用
*/
public static final List<PluginInfo> getPlugins(boolean clone) {
ArrayList<PluginInfo> array = new ArrayList<>();
Set<String> pathSet = new HashSet<>();
synchronized (PluginTable.PLUGINS) {
for (PluginInfo info : PluginTable.PLUGINS.values()) {
String path = info.getPath();
// 避免加了两次,毕竟包名和别名都会加进来
if (!pathSet.contains(path)) {
pathSet.add(path);
PluginInfo addTo;
if (clone) {
addTo = (PluginInfo) info.clone();
} else {
addTo = info;
}
array.add(addTo);
}
}
}
return array;
}
六、Client进程HOOK流程
6.1 PatchClassLoaderUtils.patch
public static boolean patch(Application application) {
// 获取Application的BaseContext
// oBase = ContextImpl
Context oBase = application.getBaseContext();
// 获取mBase.mPackageInfo
// mPackageInfo = LoadedApk
Object oPackageInfo = ReflectUtils.readField(oBase, "mPackageInfo");
// 获取mPackageInfo.mClassLoader
// mClassLoader = PathClassLoader
ClassLoader oClassLoader = (ClassLoader) ReflectUtils.readField(oPackageInfo, "mClassLoader");
// 外界可自定义ClassLoader的实现,但一定要基于RePluginClassLoader类
// create RePluginClassLoader
ClassLoader cl = RePlugin.getConfig().getCallbacks().createClassLoader(oClassLoader.getParent(), oClassLoader);
// 将新的ClassLoader写入mPackageInfo.mClassLoader
ReflectUtils.writeField(oPackageInfo, "mClassLoader", cl);
// 设置线程上下文中的ClassLoader为RePluginClassLoader
// 防止在个别Java库用到了Thread.currentThread().getContextClassLoader()时,“用了原来的PathClassLoader”,或为空指针
Thread.currentThread().setContextClassLoader(cl);
return true;
}
进程分配
静态分配
与 动态分配
4.3.1 静态分配
在Meta-data中自行声明并决定这些"插件进程"应该跑在哪个"坑位进程"内.
具体做法如下
<meta-data
android:name = "process_map"
android:value = "[
{'from':'com.package:peocess1', 'to':'$ui'},
{'from':'com.package:peocess2', 'to':'$p0'},
{'from':'com.package:peocess3', 'to':'$p1'},
{'from':'com.package:peocess4', 'to':'$p2'}
]"/>
- from: 原来声明的进程名是什么, 例如有个Activity, 其进程名声明为"com.package:peocess1"
- to: 要映射到的进程名, 必须以"$“开头, 表示"特殊进程”
$ui: 映射到UI进程
$p0: 映射到进程坑位0进程
$p1: 映射到进程坑位1进程 - 备注: 若"漏配置了"某个进程, 则该进程默认将跑在主进程中
4.3.2 动态分配
如果没有配置"静态分配"的坑位, 则默认采用"动态分配"方案
- 无需声明Meta-data, 自定义进程启动时, RePlugin会自动按顺序为其分配进程坑位
- 当坑位不足时, 无需开发者关心, RePlugin会自动处理进程情况, "动态分配"的做法和单品近乎完全一致.