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;
}