Replugin源码阅读---插件加载

513 阅读6分钟

Replugin源码阅读---框架初始化

RePlugin源码阅读---插件安装

ContentProvider

IPC学习---AIDL简单概念

插件化启动插件Activity做的工作:

  • 1、把要启动的目标Activity替换成注册在manifest上的占坑Activity(其中涉及进程选择启动, 坑位选择分配), 替换的原因是系统在startActivity的过程中会根据intent去**PackageManagerService**查询我们要启动的Activity是否已经在manifest中注册了
  • 2、在startActivity流程从ActivityManagerService回到activity所在进程时的调用流程时, 把坑位Activity替换成真正要启动的Activity
  • 3、插件化意味着类是动态加载的, 系统默认创建的ClassLoader只能加载Apk中的类即宿主中的类, 所以需要对ClassLoader进行操作使得它能加载插件中的类
RePlugin启动Activity的流程
  • 1、在宿主或插件中, 通过RePlugin.startActivity启动插件中的Activity, 会先进入常驻进程(或插件管理进程)
  • 2、在插件管理进程中, 插件管理器, 会为该Activity分配一个目标进程
  • 3、然后同步的启动该目标进程
  • 4、在插件管理进程中, 通过Binder调用, 使用目标进程留在插件管理进程中的代理, 在目标进程中为该Activity动态分配坑位(每个进程中独立管理自己的Activity坑位信息, 此时该坑位Activity还没有被AMS打开)
  • 5、把在目标进程中分配好的Activity坑位信息返回给调用方
  • 6、调用者获取到一个坑位Activity, 然后会把这个坑位Activity(带着自定义进程标签)交给AMS, 由AMS去启动
  • 7、AMS启动Activity是一个并不存在的坑位Activity, 由于宿主的ClassLoader已经在进程启动之初(Application#attachBaseContext()接口中), 被修改为我们自定义过的ClassLoader了, 当类加载器去loadClass时, 如果发现当前正在被load的Class是一个坑位Activity, 会自动分发给指定插件的ClassLoader去加载, 这样一来, 就既绕过了AMS, 也绕过了ClassLoader, 实现了: 打开一个并未安装的Activity
1.Replugin.startActivity
/**
 * 启动Intent的ComponentName的key为插件名(而不是包名), 可使用createIntent方法来
 * 创建Intent对象
 * @param intent 要打开的Activity的Intent, 其中ComponentName的Key必须为插件名
 */
public static boolean startActivity(Context context, Intent intent) {
    // 先用旧的开启Activity方案,以后再优化
    ComponentName cn = intent.getComponent();
	
    String plugin = cn.getPackageName();
    String cls = cn.getClassName();
    return Factory.startActivityWithNoInjectCN(context, intent, plugin, cls, IPluginManager.PROCESS_AUTO);
}
2.Factory.startActivityWithNoInjectCN
方法调用链:
Factory.startActivityWithNoInjectCN()
--->PluginCommImpl.startActivity()
--->PluginLibraryInternalProxy.startActivity()

/**
 * 启动一个插件中的activity, 如果插件不存在会触发下载界面
 * @param plugin 插件名
 * @param activity 待启动的activity方法
 * @param process 是否在指定进程中启动
 */
public boolean startActivity(Context context, Intent intent, String plugin, String activity, int process, boolean download) {
    /**
     * 1.是否启动下载, 若插件不可用(不存在或版本不匹配), 直接弹出"下载插件"对话框
     */
    if (download) {
        if (PluginTable.getPluginInfo(plugin) == null) {
            // 如果用户在下载即将完成时突然点按“取消”,则有可能出现插件已下载成功,但没有及时加载进来的情况
            // 因此我们会判断这种情况,如果是,则重新加载一次即可,反之则提示用户下载
            // 原因:“取消”会触发Task.release方法,最终调用mDownloadTask.destroy,导致“下载服务”的Receiver被注销,即使文件下载了也没有回调回来
            // NOTE isNeedToDownload方法会调用pluginDownloaded再次尝试加载
            if (isNeedToDownload(context, plugin)) {
            }
        }
    }
    /* 检查是否是动态注册的类 */
    // 如果要启动的 Activity 是动态注册的类,则不使用坑位机制,而是直接动态类。
    // 原因:宿主的某些动态注册的类不能运行在坑位中(如'桌面'插件的入口Activity)
    if (Factory2.isDynamicClass(plugin, activity)) {
        intent.putExtra(IPluginManager.KEY_COMPATIBLE, true);
        intent.setComponent(new ComponentName(IPC.getPackageName(), activity));
        context.startActivity(intent);
        return true;
    }
    // 如果插件状态出现问题,则每次弹此插件的Activity都应提示无法使用,或提示升级(如有新版)
    if (PluginStatusController.getStatus(plugin) < PluginStatusController.STATUS_OK) {
        return ...;
    }
    // 若为首次加载插件,且是“大插件”,则应异步加载,同时弹窗提示“加载中”
    if (!RePlugin.isPluginDexExtracted(plugin)) {
        PluginDesc pd = PluginDesc.get(plugin);
        if (pd != null && pd.isLarge()) {
            return ...;
        }
    }
    // WARNING:千万不要修改intent内容,尤其不要修改其ComponentName
    // 因为一旦分配坑位有误(或压根不是插件Activity),则外界还需要原封不动的startActivity到系统中
    // 可防止出现“本来要打开宿主,结果被改成插件”,进而无法打开宿主Activity的问题
    // 缓存打开前的Intent对象,里面将包括Action等内容
    Intent from = new Intent(intent);
    // 帮助填写打开前的Intent的ComponentName信息(如有。没有的情况如直接通过Action打开等)
    if (!TextUtils.isEmpty(plugin) && !TextUtils.isEmpty(activity)) {
        from.setComponent(new ComponentName(plugin, activity));
    }
    // 2.根据插件信息获取坑位信息
    ComponentName cn = mPluginMgr.mLocal.loadPluginActivity(intent, plugin, activity, process);
    if (cn == null) {
        return false;
    }
    // 将Intent指向到“坑位”。这样:
    // from:插件原Intent
    // to:坑位Intent
    intent.setComponent(cn);
    context.startActivity(intent);
    // 通知外界,已准备好要打开Activity了
    // 其中:from为要打开的插件的Intent,to为坑位Intent
    return true;
}
3.PluginCommImpl.loadPluginActivity
/**
 * 加载插件Activity,在startActivity之前调用
 * @param plugin 插件名
 * @param target 目标Service名,如果传null,则取获取到的第一个
 * @param process 是否在指定进程中启动
 */
public ComponentName loadPluginActivity(Intent intent, String plugin, String activity, int process) {
    ActivityInfo ai = null;
    String container = null;
    PluginBinderInfo info = new PluginBinderInfo(PluginBinderInfo.ACTIVITY_REQUEST);
    // 获取ActivityInfo(可能是其它插件的 Activity,所以这里使用 pair 将 pluginName 也返回)
    ai = getActivityInfo(plugin, activity, intent);
    if (ai == null) {
        return null;
    }
    // 存储此 Activity 在插件 Manifest 中声明主题到 Intent
    intent.putExtra(INTENT_KEY_THEME_ID, ai.theme);
    /** 
     * 根据 activity 的 processName,选择进程 ID 标识
     * 在Loader.loadDex加载插件的时候调用adjustPluginProcess(mPackageInfo.applicationInfo)
     * 调整activity的目标进程.(根据组件的目标进程填写, 判断是在UI进程, 还是在常驻进程还是在自定
     * 义进程, 其中自定义进程需要把进程映射成p0, p1, p2这几个指定的进程)
     */
    if (ai.processName != null) {
        process = PluginClientHelper.getProcessInt(ai.processName);
    }
    // 容器选择(启动目标进程)
    IPluginClient client = MP.startPluginProcess(plugin, process, info);
    if (client == null) {
        return null;
    }
    // 远程分配坑位
    container = client.allocActivityContainer(plugin, process, ai.name, intent);
    PmBase.cleanIntentPluginParams(intent);
    // 指向在宿主manifest中注册好的组件坑位
    return new ComponentName(IPC.getPackageName(), container);
}
3.1 PluginCommImpl.getActivityInfo
/**
 * 根据条件,查找 ActivityInfo 对象
 * @param plugin   插件名称
 * @param activity Activity 名称
 * @param intent   调用者传递过来的 Intent
 * @return 插件中 Activity 的 ActivityInfo
 */
public ActivityInfo getActivityInfo(String plugin, String activity, Intent intent) {
	// 获取插件对象
    Plugin p = mPluginMgr.loadAppPlugin(plugin);
    if (p == null) {
        return null;
    }
    ActivityInfo ai = null;
    // activity 不为空时,从插件声明的 Activity 集合中查找
    if (!TextUtils.isEmpty(activity)) {
        ai = p.mLoader.mComponents.getActivity(activity);
    } else {
        // activity 为空时,根据 Intent 匹配
        ai = IntentMatcherHelper.getActivityInfo(mContext, plugin, intent);
    }
    return ai;
}
3.2 PmBase.loadAppPlugin
4.MP.startPluginProcess
方法调用链:
MP.startPluginProcess()
---> PmHostSvc.startPluginProcess()
---> PmBase.startPluginProcessLocked()

final IPluginClient startPluginProcessLocked(String plugin, int process, PluginBinderInfo info) {
    // 如果是activity, binder这类请求, 没有指定进程则强制使用UI进程
    if (Constant.ENABLE_PLUGIN_ACTIVITY_AND_BINDER_RUN_IN_MAIN_UI_PROCESS) {
        if (info.request == PluginBinderInfo.ACTIVITY_REQUEST) {
            if (process == IPluginManager.PROCESS_AUTO) {
                process = IPluginManager.PROCESS_UI;
            }
        }
        if (info.request == PluginBinderInfo.BINDER_REQUEST) {
            if (process == IPluginManager.PROCESS_AUTO) {
                process = IPluginManager.PROCESS_UI;
            }
        }
    }       
    // 根据插件名称, 从已创建的进程中获取目标进程, 如果已存在且还存活则直接返回
    IPluginClient client = PluginProcessMain.probePluginClient(plugin, process, info);
    if (client != null) {
        return client;
    }
    int index = IPluginManager.PROCESS_AUTO;
    /**
     * 分配进程
     * 两种情况: 1.ui进程、2.自定义进程(需要在manifest中将自定义进程映射成p0,p1,p2)
     */
    index = PluginProcessMain.allocProcess(plugin, process);
    // 分配的坑位不属于UI、自定义进程或Stub坑位进程,就返回。(没找到有效进程)
    if (!(index == IPluginManager.PROCESS_UI
                || PluginProcessHost.isCustomPluginProcess(index)
                || PluginManager.isPluginProcess(index))) {
        return null;
    }
    // 根据上面分配的进程index, 组装成一个uri来启动预先在manifest中埋好的provider, 因为
    // provider在manifest指定了目标进程, 从而间接达到开启进程的效果
    boolean rc = PluginProviderStub.proxyStartPluginProcess(mContext, index);
    if (!rc) {
        return null;
    }
    // 再次获取
    client = PluginProcessMain.probePluginClient(plugin, process, info);
    if (client == null) {
        return null;
    }
    return client;
}
4.1自定义进程
<meta-data 
	android:name="process_map"
	android:value="[{'from':'com.qihoo360.replugin.sample.demo3:bg','to':'$p0'}]"/>
5.PluginProcessPer.allocActivityContainer
public String allocActivityContainer(String plugin, int process, String target, Intent intent) throws RemoteException {
    // 一旦有分配,则进入监控状态(一是避免不退出的情况,二也是最重要的是避免现在就退出的情况)
    RePlugin.getConfig().getEventCallbacks().onPrepareAllocPitActivity(intent);
    String loadPlugin = null;
    String container = bindActivity(loadPlugin, process, target, intent);
    return container;
}
6.PluginProcessPer.bindActivity
/**
 * 加载插件;找到目标Activity;搜索匹配容器;加载目标Activity类;建立临时映射;返回容器
 * @param plugin   插件名称
 * @param process  进程
 * @param activity Activity 名称
 * @param intent   调用者传入的 Intent
 * @return 坑位
 */
final String bindActivity(String plugin, int process, String activity, Intent intent) {
    /* 获取插件对象 */
    Plugin p = mPluginMgr.loadAppPlugin(plugin);
    /* 获取 ActivityInfo */
    ActivityInfo ai = p.mLoader.mComponents.getActivity(activity);
    if (ai == null) {
        return null;
    }
    if (ai.processName == null) {
        ai.processName = ai.applicationInfo.processName;
    }
    if (ai.processName == null) {
        ai.processName = ai.packageName;
    }
    /* 获取 Container */
    String container;
    // 根据是自定义进程还是UI进程, 取不同的坑位信息
    if (ai.processName.contains(PluginProcessHost.PROCESS_PLUGIN_SUFFIX2)) {
		String processTail = PluginProcessHost.processTail(ai.processName);
		container = mACM.alloc2(ai, plugin, activity, process, intent, processTail);
	} else {
		container = mACM.alloc(ai, plugin, activity, process, intent);
	}
	// 检查activity是否存在, 这个ClassLoader是PluginDexClassLoader
    Class<?> c = null;
    c = p.mLoader.mClassLoader.loadClass(activity);
    return container;
}
7.PluginContainers.alloc
final String alloc(ActivityInfo ai, String plugin, String activity, int process, Intent intent) {
	ActivityState state;
    String defaultPluginTaskAffinity = ai.applicationInfo.packageName;
    /* SingleInstance 优先级最高 */
	synchronized (mLock) {
		state = allocLocked(ai, mLaunchModeStates.getStates(ai.launchMode, ai.theme), plugin, activity, intent);
	}
    if (state != null) {
        return state.container;
    }
    return null;
}
8.PluginContainers.allocLocked
private final ActivityState allocLocked(ActivityInfo ai, HashMap<String, ActivityState> map,
                                            String plugin, String activity, Intent intent) {
    // 首先找上一个活的,或者已经注册的,避免多个坑到同一个activity的映射
    for (ActivityState state : map.values()) {
        if (state.isTarget(plugin, activity)) {
			return state;
		}
	}
    // 新分配:找空白的,第一个
    for (ActivityState state : map.values()) {
        if (state.state == STATE_NONE) {
            state.occupy(plugin, activity);
            return state;
        }
    }
    ActivityState found;
    // 重用:则找最老的那个
    found = null;
    for (ActivityState state : map.values()) {
        if (!state.hasRef()) {
            if (found == null) {
                found = state;
            } else if (state.timestamp < found.timestamp) {
                found = state;
            }
        }
    }
    if (found != null) {
		found.occupy(plugin, activity);
		return found;
    }
	// 强挤:最后一招,挤掉:最老的那个
	found = null;
	for (ActivityState state : map.values()) {
		if (found == null) {
            found = state;
        } else if (state.timestamp < found.timestamp) {
            found = state;
        }
    }
    if (found != null) {
        found.finishRefs();
        found.occupy(plugin, activity);
        return found;
    }
    // never reach here
    return null;
}
8.1 ActivityState.occupy
private final void occupy(String plugin, String activity) {
	this.state = STATE_OCCUPIED;
	this.plugin = plugin;
	this.activity = activity;
	cleanRefs();
	this.timestamp = System.currentTimeMillis();
	//
	save2Pref(this.plugin, this.activity, this.container);
}
9.ActivityThread.performLaunchActivity

启动坑位Activity最后会调用ActivityThread.performLaunchActivity

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
	ActivityInfo aInfo = r.activityInfo;
    if (r.packageInfo == null) {
    	// 获取LoaderApk, 此时LoadedApk中的ClassLoader已经是RePluginClassLoader了
        r.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo,
                    Context.CONTEXT_INCLUDE_CODE);
    }
    //创建Context的时候把ClassLoader设置为r.packageInfo.getClassLoader()
    ContextImpl appContext = createBaseContextForActivity(r);
    Activity activity = null;
    java.lang.ClassLoader cl = appContext.getClassLoader();
    activity = mInstrumentation.newActivity(cl, component.getClassName(), r.intent);
    r.intent.setExtrasClassLoader(cl);
    r.intent.prepareToEnterProcess();
    if (r.state != null) {
		r.state.setClassLoader(cl);
	}
	...
}
10.RePluginClassLoader.loadClass
protected Class<?> loadClass(String className, boolean resolve) {
	Class<?> c = null;
    //先调用PmBase.loadClass 对应插件classLoader加载类或者是加载动态注册类
	c = PMF.loadClass(className, resolve);
	if (c != null) {
        return c;
    }
	c = mOrig.loadClass(className);
	return c;
}
11.PMF.loadClass
final Class<?> loadClass(String className, boolean resolve) {
	/**
     * 判断是否为坑位Activity
     */
    if (mContainerActivities.contains(className)) {
    	// 加载实际Activity
        Class<?> c = mClient.resolveActivityClass(className);
		if (c != null) {
			return c;
		}
		return DummyActivity.class;
	}
    // 插件定制表
    DynamicClass dc = mDynamicClasses.get(className);
    if (dc != null) {
        final Context context = RePluginInternal.getAppContext();
        PluginDesc desc = PluginDesc.get(dc.plugin);
        // 加载动态类时,如果其对应的插件未下载,则转到代理类
        if (desc != null) {
            String plugin = desc.getPluginName();
            if (PluginTable.getPluginInfo(plugin) == null) {
                return DynamicClassProxyActivity.class;
            }
        }
        // return dummy class
        if ("activity".equals(dc.classType)) {
        	return DummyActivity.class;
        } else if ("service".equals(dc.classType)) {
        	return DummyService.class;
        } else if ("provider".equals(dc.classType)) {
        	return DummyProvider.class;
        }
        return dc.defClass;
    }
    //
    return loadDefaultClass(className);
}
12.PluginProcessPer.resolveActivityClass
/**
 * 类加载器根据容器解析到目标的activity
 */
final Class<?> resolveActivityClass(String container) {
	String plugin = null;
	String activity = null;
	// 先找登记的,如果找不到,则用forward activity
    /**
     * 根据containers找之前bindActivity登记的ActivityState从而找到对应插件和activity,
     * 如果找不到, 则用forward activity
     */
	PluginContainers.ActivityState state = mACM.lookupByContainer(container);
	if (state == null) {
		return ForwardActivity.class;
	}
	plugin = state.plugin;
	activity = state.activity;
	Plugin p = mPluginMgr.loadAppPlugin(plugin);
	ClassLoader cl = p.getClassLoader();
	Class<?> c = null;
    //对应插件来加载activity类
	c = cl.loadClass(activity);
	return c;
}