Replugin-框架初始化流程

一、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会自动处理进程情况, "动态分配"的做法和单品近乎完全一致.
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值