Replugin源码解析
一、ClassLoader介绍
有3个,BootClassLoader,PathClassLoader,DexClassLoader
- BootClassLoader
用来加载系统类,如Android中各种SystemManager各种Service类,对于所有应用来说加载到的都是同一个系统类,这个BootClassLoader是属于系统的,应用无法使用,防止应用随意加载系统级的类。 - PathClassLoader
应用级的,是系统为每个应用分配的ClassLoader,用来加载已经安装到系统上的dex文件中的类,如果是一个未经过安装的apk,其中的dex文件中的类不可以被加载 - DexClassLoader
应用级的,应用可以自己创建此ClassLoader的实例,去加载未经过安装的dex文件中的类。
todo继承关系
二、RePlugin中的ClassLoader
github地址 https://github.com/Qihoo360/RePlugin
主要有2个
1、RePluginClassLoader继承自PathClassLoader,宿主的ClassLoader,插件框架的核心之一。
2、PluginDexClassLoader继承自DexClassLoader,插件的DexClassLoader。
1 启动插件activity流程源码分析
先看构造方法,将系统的ClassLoader属性和方法都拷贝了过来
public RePluginClassLoader(ClassLoader parent, ClassLoader orig) {
// 由于PathClassLoader在初始化时会做一些Dir的处理,所以这里必须要传一些内容进来
// 但我们最终不用它,而是拷贝所有的Fields
super("", "", parent);
mOrig = orig;//先将系统分配的ClassLoader缓存起来
// 将原来宿主里的关键字段,拷贝到这个对象上,这样骗系统以为用的还是以前的东西(尤其是DexPathList)
// 注意,这里用的是“浅拷贝”
copyFromOriginal(orig);
//通过反射将原始的ClassLoader中的方法反射出来
initMethods(orig);
}
RePluginClassLoader主要重写了loadClass方法
@Override
protected Class<?> loadClass(String className, boolean resolve) throws ClassNotFoundException {
//
Class<?> c = null;
c = PMF.loadClass(className, resolve);
if (c != null) {
return c;
}
//
try {
c = mOrig.loadClass(className);
// 只有开启“详细日志”才会输出,防止“刷屏”现象
if (LogDebug.LOG && RePlugin.getConfig().isPrintDetailLog()) {
LogDebug.d(TAG, "loadClass: load other class, cn=" + className);
}
return c;
} catch (Throwable e) {
//
}
//
return super.loadClass(className, resolve);
}
加载dex文件中的类,在加载时候每次都是通过PMF.loadClass,PMF是插件的管理类,每次先去插件中加载,优先使用插件中的类,如果没找到,就从宿主工程中用缓存下来的原始的ClassLoader加载。
接下来看看RePluginClassLoader是在什么时候对系统的ClassLoader进行的替换
在RePluginCallbacks中的createClassLoader方法创建RePluginClassLoader的实例
/**
* 创建【宿主用的】 RePluginClassLoader 对象以支持大多数插件化特征。默认为:RePluginClassLoader的实例
* <p>
* 子类可复写此方法,来创建自己的ClassLoader,做相应的事情(如Hook宿主的ClassLoader做一些特殊的事)
*
* @param parent 该ClassLoader的父亲,通常为BootClassLoader
* @param original 宿主的原ClassLoader,通常为PathClassLoader
* @return 支持插件化方案的ClassLoader对象,可直接返回RePluginClassLoader
* @see RePluginClassLoader
*/
public RePluginClassLoader createClassLoader(ClassLoader parent, ClassLoader original) {
return new RePluginClassLoader(parent, original);
}
接下来在PatchClassLoaderUtils调用,在patch方法中将application对象的PackageInfo对象的ClassLoader替换成自己的
public static boolean patch(Application application) {
try {
// 获取Application的BaseContext (来自ContextWrapper)
Context oBase = application.getBaseContext();
...
// 通过反射获取mBase.mPackageInfo
// 1. ApplicationContext - Android 2.1
// 2. ContextImpl - Android 2.2 and higher
// 3. AppContextImpl - Android 2.2 and higher
Object oPackageInfo = ReflectUtils.readField(oBase, "mPackageInfo");
...
// mPackageInfo的类型主要有两种:
// 1. android.app.ActivityThread$PackageInfo - Android 2.1 - 2.3
// 2. android.app.LoadedApk - Android 2.3.3 and higher
// 通过反射获取mPackageInfo.mClassLoader
ClassLoader oClassLoader = (ClassLoader) ReflectUtils.readField(oPackageInfo, "mClassLoader");
...
//获取到一个新的ClassLoader。 外界可自定义ClassLoader的实现,但一定要基于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);
}
...
}
PMF类初始化init方法中调用了PatchClassLoaderUtils.patch 方法,然后在RePlugin类attachBaseContext方法中调用了PMF.init方法,然后在RePluginApplication类中attachBaseContext方法中调用RePlugin.App.attachBaseContext。我们的应用Application继承了RePluginApplication,所以在应用刚启动时候就会执行替换ClassLoader
这个自己的ClassLoader如何加载插件中的类
启动插件中Activity流程
先看RePlugin的startActivity方法
/**
* 开启一个插件的Activity <p>
* 其中Intent的ComponentName的Key应为插件名(而不是包名),可使用createIntent方法来创建Intent对象
*
* @param context Context对象
* @param intent 要打开Activity的Intent,其中ComponentName的Key必须为插件名
* @return 插件Activity是否被成功打开?
*/
public static boolean startActivity(Context context, Intent intent) {
// TODO 先用旧的开启Activity方案,以后再优化
ComponentName cn = intent.getComponent();
if (cn == null) {
// TODO 需要支持Action方案
return false;
}
String plugin = cn.getPackageName();
String cls = cn.getClassName();
return Factory.startActivityWithNoInjectCN(context, intent, plugin, cls, IPluginManager.PROCESS_AUTO);
}
接着看下Factory.startActivityWithNoInjectCN方法,核心是sPluginManager.startActivity方法,里面又调用了PluginLibraryInternalProxy的startActivity方法
public boolean startActivity(Context context, Intent intent, String plugin, String activity, int process, boolean download) {
// 是否启动下载
// 若插件不可用(不存在或版本不匹配),则直接弹出“下载插件”对话框
// 因为已经打开UpdateActivity,故在这里返回True,告诉外界已经打开,无需处理
if (download) {
if (PluginTable.getPluginInfo(plugin) == null) {
...
// 如果用户在下载即将完成时突然点按“取消”,则有可能出现插件已下载成功,但没有及时加载进来的情况
// 因此我们会判断这种情况,如果是,则重新加载一次即可,反之则提示用户下载
// 原因:“取消”会触发Task.release方法,最终调用mDownloadTask.destroy,导致“下载服务”的Receiver被注销,即使文件下载了也没有回调回来
// NOTE isNeedToDownload方法会调用pluginDownloaded再次尝试加载
if (isNeedToDownload(context, plugin)) {
return RePlugin.getConfig().getCallbacks().onPluginNotExistsForActivity(context, plugin, intent, process);
}
}
}
/* 检查是否是动态注册的类 */
// 如果要启动的 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都应提示无法使用,或提示升级(如有新版)
// Added by Jiongxuan Zhang
if (PluginStatusController.getStatus(plugin) < PluginStatusController.STATUS_OK) {
...
return RePlugin.getConfig().getCallbacks().onPluginNotExistsForActivity(context, plugin, intent, process);
}
// 若为首次加载插件,且是“大插件”,则应异步加载,同时弹窗提示“加载中”
// Added by Jiongxuan Zhang
if (!RePlugin.isPluginDexExtracted(plugin)) {
PluginDesc pd = PluginDesc.get(plugin);
if (pd != null && pd.isLarge()) {
...
return RePlugin.getConfig().getCallbacks().onLoadLargePluginForActivity(context, plugin, intent, process);
}
}
// 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));
}
//插件activity查找
ComponentName cn = mPluginMgr.mLocal.loadPluginActivity(intent, plugin, activity, process);
...
// 将Intent指向到“坑位”。这样:
// from:插件原Intent
// to:坑位Intent
intent.setComponent(cn);
...
context.startActivity(intent);//里面是Android原生逻辑,启动坑activity
// 通知外界,已准备好要打开Activity了
// 其中:from为要打开的插件的Intent,to为坑位Intent
RePlugin.getConfig().getEventCallbacks().onPrepareStartPitActivity(context, from, intent);
return true;
}
看下PluginCommImpl的loadPluginActivity方法是如何获取插件activity
/**
* 加载插件Activity,在startActivity之前调用
* @param intent
* @param plugin 插件名
* @param target 目标Service名,如果传null,则取获取到的第一个
* @param process 是否在指定进程中启动
* @return
*/
public ComponentName loadPluginActivity(Intent intent, String plugin, String activity, int process) {
ActivityInfo ai = null;
String container = null;
PluginBinderInfo info = new PluginBinderInfo(PluginBinderInfo.ACTIVITY_REQUEST);
try {
// 获取 ActivityInfo(可能是其它插件的 Activity,所以这里使用 pair 将 pluginName 也返回)
ai = getActivityInfo(plugin, activity, intent);
...
// 存储此 Activity 在插件 Manifest 中声明主题到 Intent
intent.putExtra(INTENT_KEY_THEME_ID, ai.theme);
// 根据 activity 的 processName,选择进程 ID 标识
if (ai.processName != null) {
process = PluginClientHelper.getProcessInt(ai.processName);
}
// 容器选择(启动目标进程)
IPluginClient client = MP.startPluginProcess(plugin, process, info);
if (client == null) {
return null;
}
// 远程分配坑位,就是由于我们的activity没有在Manifist注册,坑位就是宿主中提前注册好的假activity匹配到获取的activity
container = client.allocActivityContainer(plugin, process, ai.name, intent);
}
...
PmBase.cleanIntentPluginParams(intent);
//将获取的activity信息拼装到自己的Intent
PluginIntent ii = new PluginIntent(intent);
ii.setPlugin(plugin);
ii.setActivity(ai.name);
ii.setProcess(IPluginManager.PROCESS_AUTO);
ii.setContainer(container);
ii.setCounter(0);
return new ComponentName(IPC.getPackageName(), container);
}
看下getActivityInfo方法
public ActivityInfo getActivityInfo(String plugin, String activity, Intent intent) {
// 获取插件对象
Plugin p = mPluginMgr.loadAppPlugin(plugin);
...
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;
}
看下PmBase的loadAppPlugin方法,最终是调用Plugin的load方法
final boolean load(int load, boolean useCache) {
PluginInfo info = mInfo;
boolean rc = loadLocked(load, useCache);//完成插件的安装
// 尝试在此处调用Application.onCreate方法
if (load == LOAD_APP && rc) {
callApp();
}
// 如果info改了,通知一下常驻
// 只针对P-n的Type转化来处理,一定要通知,这样Framework_Version也会得到更新
if (rc && mInfo != info) {
UpdateInfoTask task = new UpdateInfoTask((PluginInfo) mInfo.clone());
Tasks.post2Thread(task);
}
return rc;
}
看下loadLocked方法核心方法是里面的doLoad方法,里面的核心逻辑是Loader类的loadDex方法
final boolean loadDex(ClassLoader parent, int load) {
try {
PackageManager pm = mContext.getPackageManager();
mPackageInfo = Plugin.queryCachedPackageInfo(mPath);
if (mPackageInfo == null) {
// 创建PackageInfo
mPackageInfo = pm.getPackageArchiveInfo(mPath,
PackageManager.GET_ACTIVITIES | PackageManager.GET_SERVICES | PackageManager.GET_PROVIDERS | PackageManager.GET_RECEIVERS | PackageManager.GET_META_DATA);
...
mPackageInfo.applicationInfo.sourceDir = mPath;
mPackageInfo.applicationInfo.publicSourceDir = mPath;
if (TextUtils.isEmpty(mPackageInfo.applicationInfo.processName)) {
mPackageInfo.applicationInfo.processName = mPackageInfo.applicationInfo.packageName;
}
// 添加针对SO库的加载
// 此属性最终用于ApplicationLoaders.getClassLoader,在创建PathClassLoader时成为其参数
// 这样findLibrary可不用覆写,即可直接实现SO的加载
PluginInfo pi = mPluginObj.mInfo;
File ld = pi.getNativeLibsDir();
mPackageInfo.applicationInfo.nativeLibraryDir = ld.getAbsolutePath();
...
// 创建或获取ComponentList表
// 将插件中所有清单文件中注册的组件初始化到mComponents
mComponents = Plugin.queryCachedComponentList(mPath);
if (mComponents == null) {
// ComponentList
mComponents = new ComponentList(mPackageInfo, mPath, mPluginObj.mInfo);
// 动态注册插件中声明的 receiver
regReceivers();
...
}
if (load == Plugin.LOAD_INFO) {
return isPackageInfoLoaded();
}
//初始化插件中Resources
mPkgResources = Plugin.queryCachedResources(mPath);
// LOAD_RESOURCES和LOAD_ALL都会获取资源,但LOAD_INFO不可以(只允许获取PackageInfo)
if (mPkgResources == null) {
// Resources
try {
if (BuildConfig.DEBUG) {
// 如果是Debug模式的话,防止与Instant Run冲突,资源重新New一个
Resources r = pm.getResourcesForApplication(mPackageInfo.applicationInfo);
mPkgResources = new Resources(r.getAssets(), r.getDisplayMetrics(), r.getConfiguration());
} else {
mPkgResources = pm.getResourcesForApplication(mPackageInfo.applicationInfo);
}
} ...
}
//查找ClassLoader
mClassLoader = Plugin.queryCachedClassLoader(mPath);
if (mClassLoader == null) {
// 创建ClassLoader
...
mClassLoader = RePlugin.getConfig().getCallbacks().createPluginClassLoader(mPluginObj.mInfo, mPath, out, soDir, parent);
}
// 获取对应插件Context
mPkgContext = new PluginContext(mContext, android.R.style.Theme, mClassLoader, mPkgResources, mPluginName, this);
} ...
return true;
}
一个插件在被首次加载的时候一层层调用,最终会这个Loader类的loadDex方法创建插件对应的上下文Context,这个PluginContext里有mClassLoader, mPkgResources, mPluginName,有ClassLoader就可以加载class,有PkgResources就可以加载插件中资源,到这里插件就被安装初始化完毕了,完毕后,这个插件就可以被正常使用了。
回到getActivityInfo,获取到信息后就能得到manifest中注册的组件,将组件初始化到ComponentList,loadPluginActivity就可以获取插件activity。
因为activity是插件中的没有注册,直接启动会报错,所以要分配坑位,坑位就是宿主中提前注册好的假activity匹配到获取的activity。接下来看下是如何分配坑位的
allocActivityContainer方法实际是IPluginClient这个aidl文件定义的接口,PluginProcessPer类中实现核心是bindActivity方法
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.processName == null) {
ai.processName = ai.applicationInfo.processName;
}
if (ai.processName == null) {
ai.processName = ai.packageName;
}
/* 获取 Container */
String container;
// 自定义进程
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 是否存在 */
Class<?> c = null;
try {
c = p.mLoader.mClassLoader.loadClass(activity);
} ...
return container;
}
看下alloc2方法,里面最终调allocLocked方法完成分配
private final ActivityState allocLocked(ActivityInfo ai, HashMap<String, ActivityState> map,
String plugin, String activity, Intent intent) {
// 坑和状态的 map 为空
if (map == null) {
...
return null;
}
// 首先找上一个活的,或者已经注册的,避免多个坑到同一个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;
}
...
}
找到坑位后,回到startActivity方法里的 context.startActivity(intent)方法,里面是Android原生逻辑,启动坑activity。看下系统activity启动流程,Activity的startActivity会调用到startActivityForResult
public void startActivityForResult(@RequiresPermission Intent intent, int requestCode,
@Nullable Bundle options) {
if (mParent == null) {
options = transferSpringboardActivityOptions(options);
Instrumentation.ActivityResult ar =
mInstrumentation.execStartActivity(
this, mMainThread.getApplicationThread(), mToken, this,
intent, requestCode, options);
....
}
}
里面是通过Instrumentation.execStartActivity启动activity
public ActivityResult execStartActivity(
Context who, IBinder contextThread, IBinder token, Activity target,
Intent intent, int requestCode, Bundle options) {
....
try {
intent.migrateExtraStreamToClipData();
intent.prepareToLeaveProcess(who);
int result = ActivityManager.getService()
.startActivity(whoThread, who.getBasePackageName(), intent,
intent.resolveTypeIfNeeded(who.getContentResolver()),
token, target != null ? target.mEmbeddedID : null,
requestCode, 0, null, options);
checkStartActivityResult(result, intent);
} ...
}
核心启动方法是ActivityManager.getService().startActivity,这个是真正完成activity启动的方法,这个时候就已经到了系统进程中,activity的启动是跨进程的binder通信。Service主要是检查activity是否注册过,我们启动的坑位activity是注册过的,会正常启动。然后回到ActivityThread,里面的performLaunchActivity方法真正完成activity加载和创建
/** Core implementation of activity launch. */
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
ActivityInfo aInfo = r.activityInfo;
...
ContextImpl appContext = createBaseContextForActivity(r);
Activity activity = null;
try {
java.lang.ClassLoader cl = appContext.getClassLoader();
activity = mInstrumentation.newActivity(
cl, component.getClassName(), r.intent);
.....
}
mInstrumentation.newActivity方法里的instantiateActivity方法完成创建
public @NonNull Activity instantiateActivity(@NonNull ClassLoader cl, @NonNull String className,
@Nullable Intent intent)
throws InstantiationException, IllegalAccessException, ClassNotFoundException {
return (Activity) cl.loadClass(className).newInstance();
}
前面的过程都是通过坑位activity骗过系统的检查,然后通过这个classLoader完成坑位的替换,创建的时候创建成我们的activity,核心逻辑回到了自定义的classLoader中,接下来看下PluginDexClassLoader,看他的loadClass方法
@Override
protected Class<?> loadClass(String className, boolean resolve) throws ClassNotFoundException {
// 插件自己的Class。从自己开始一直到BootClassLoader,采用正常的双亲委派模型流程,读到了就直接返回
Class<?> pc = null;
ClassNotFoundException cnfException = null;
try {
pc = super.loadClass(className, resolve);
if (pc != null) {
return pc;
}
} catch (ClassNotFoundException e) {
// Do not throw "e" now
cnfException = e;
if (PluginDexClassLoaderPatch.need2LoadFromHost(className)) {
try {
return loadClassFromHost(className, resolve);
} ...
}
}
// 若插件里没有此类,则会从宿主ClassLoader中找,找到了则直接返回
// 注意:需要读取isUseHostClassIfNotFound开关。默认为关闭的。可参见该开关的说明
if (RePlugin.getConfig().isUseHostClassIfNotFound()) {
try {
return loadClassFromHost(className, resolve);
}...
}
2 启动插件service流程源码分析
看下PluginContext类的startService方法
@Override
public ComponentName startService(Intent service) {
if (mContextInjector != null) {
mContextInjector.startServiceBefore(service);
}
if (mLoader.mPluginObj.mInfo.getFrameworkVersion() <= 2) {
// 仅框架版本为3及以上的才支持
return super.startService(service);
}
try {
return PluginServiceClient.startService(this, service, true);
} catch (PluginClientHelper.ShouldCallSystem e) {
// 若打开插件出错,则直接走系统逻辑
return super.startService(service);
} finally {
if (mContextInjector != null) {
mContextInjector.startServiceAfter(service);
}
}
}
核心方法是PluginServiceClient.startService
public static ComponentName startService(Context context, Intent intent, boolean throwOnFail) {
// 从 Intent 中获取 ComponentName
ComponentName cn = getServiceComponentFromIntent(context, intent);
// 获取将要使用服务的进程ID,并在稍后获取PSS对象
int process = getProcessByComponentName(cn);
if (process == PROCESS_UNKNOWN) {
// 有可能是不支持的插件,也有可能本意就是想调用Main工程的服务。则直接调用系统方法来做
if (throwOnFail) {
throw new PluginClientHelper.ShouldCallSystem();
} else {
return context.startService(intent);
}
}
// 既然确认是插件,则将之前获取的插件信息填入
intent.setComponent(cn);
//AIDL的调用
IPluginServiceServer pss = sServerFetcher.fetchByProcess(process);
...
// 开启服务
try {
return pss.startService(intent, sClientMessenger);
} ...
}
getProcessByComponentName中的核心方法是PluginClientHelper.getProcessInt
public static Integer getProcessInt(String processName) {
if (!TextUtils.isEmpty(processName)) {
// 插件若想将组件定义成在"常驻进程"中运行,则可以在android:process中定义:
// 1. (推荐)":GuardService"。这样无论宿主的常驻进程名是什么,都会定向到"常驻进程"
String pntl = processName.toLowerCase();
String ppdntl = HostConfigHelper.PERSISTENT_NAME.toLowerCase();
if (pntl.contains(ppdntl)) {
return IPluginManager.PROCESS_PERSIST;
}
// 2. 和宿主常驻进程名相同,这样也会定向到"常驻进程",但若移植到其它宿主上则会出现问题
String ppntl = IPC.getPersistentProcessName().toLowerCase();
if (TextUtils.equals(pntl, ppntl)) {
return IPluginManager.PROCESS_PERSIST;
}
// 3. 用户自定义进程时,从 Map 中取数据
// (根据冒号之后的名称来判断是否是自定义进程)
processName = PluginProcessHost.processTail(processName.toLowerCase());
if (PROCESS_INT_MAP.containsKey(processName)) {
return PROCESS_INT_MAP.get(processName);
}
}
return IPluginManager.PROCESS_UI;
}
看下PluginServiceServer中AIDL的实现class Stub extends IPluginServiceServer.Stub
里面的startServiceLocked方法实现了启动Service
// 启动插件Service。说明见PluginServiceClient的定义
ComponentName startServiceLocked(Intent intent, Messenger client) {
intent = cloneIntentLocked(intent);
ComponentName cn = intent.getComponent();
final ServiceRecord sr = retrieveServiceLocked(intent);
if (sr == null) {
return null;
}
if (!installServiceIfNeededLocked(sr)) {
return null;
}
sr.startRequested = true;
// 加入到列表中,统一管理
mServicesByName.put(cn, sr);
// 从binder线程post到ui线程,去执行Service的onStartCommand操作
Message message = mHandler.obtainMessage(WHAT_ON_START_COMMAND);
Bundle data = new Bundle();
data.putParcelable("intent", intent);
message.setData(data);
message.obj = sr;
mHandler.sendMessage(message);
return cn;
}
installServiceIfNeededLocked方法里核心是installServiceLocked方法,看下是如何创建service的
// 加载插件,获取Service对象,并将其缓存起来
private boolean installServiceLocked(ServiceRecord sr) {
// 通过ServiceInfo创建Service对象
Context plgc = Factory.queryPluginContext(sr.plugin);
...
ClassLoader cl = plgc.getClassLoader();
...
// 构建Service对象
Service s;
try {
s = (Service) cl.loadClass(sr.serviceInfo.name).newInstance();
} ...
// 只复写Context,别的都不做
try {
attachBaseContextLocked(s, plgc);//通过反射调用Service.attachBaseContext方法(Protected的)换成插件的context
}...
s.onCreate();//调用onCreate生命周期
sr.service = s;
// 开启“坑位”服务,防止进程被杀
ComponentName pitCN = getPitComponentName();
sr.pitComponentName = pitCN;
startPitService(pitCN);
return true;
}
3 自定义进程源码分析
看下PluginServiceServerFetcher的fetchByProcess方法
public IPluginServiceServer fetchByProcess(int process) {
// 取之前的缓存
IPluginServiceServer pss;
synchronized (PSS_LOCKER) {
pss = mServiceManagerByProcessMap.get(process);
if (pss != null) {
return pss;
}
}
// 缓存没有?则去目标进程获取新的
try {
if (process == IPluginManager.PROCESS_PERSIST) {//是否是常驻进程
IPluginHost ph = PluginProcessMain.getPluginHost();
pss = ph.fetchServiceServer();
} else {
PluginBinderInfo pbi = new PluginBinderInfo(PluginBinderInfo.NONE_REQUEST);
IPluginClient pc = MP.startPluginProcess(null, process, pbi);
pss = pc.fetchServiceServer();
}
// 挂死亡周期,如果出问题了就置空重来,防止外界调用psm出现DeadObject问题
pss.asBinder().linkToDeath(new PSSDeathMonitor(process, pss.asBinder()), 0);
} ...
if (pss != null) {
synchronized (PSS_LOCKER) {
mServiceManagerByProcessMap.put(process, pss);
}
}
return pss;
}
MP.startPluginProcess方法看进程分配和启动,里面调用了startPluginProcess,startPluginProcess方法的实现最终会走到PmBase的startPluginProcessLocked方法
final IPluginClient startPluginProcessLocked(String plugin, int process, PluginBinderInfo info) {
...
// 获取
IPluginClient client = PluginProcessMain.probePluginClient(plugin, process, info);
if (client != null) {
...
return client;
}
// 分配
int index = IPluginManager.PROCESS_AUTO;
try {
index = PluginProcessMain.allocProcess(plugin, process);
...
} ...
// 分配的坑位不属于UI、自定义进程或Stub坑位进程,就返回。(没找到有效进程)
if (!(index == IPluginManager.PROCESS_UI
|| PluginProcessHost.isCustomPluginProcess(index)
|| PluginManager.isPluginProcess(index))) {
return null;
}
// 启动
boolean rc = PluginProviderStub.proxyStartPluginProcess(mContext, index);
...
// 再次获取
client = PluginProcessMain.probePluginClient(plugin, process, info);
if (client == null) {
...
return null;
}
...
return client;
}
看下分配逻辑的allocProcess方法最终调用StubProcessManager.allocProcess方法
static final int allocProcess(String plugin) {
if (LOG) {
LogDebug.d(PLUGIN_TAG, "alloc plugin process: plugin=" + plugin);
}
// 取运行列表
List<ActivityManager.RunningAppProcessInfo> processes = AMSUtils.getRunningAppProcessesNoThrows(RePluginInternal.getAppContext());
// 取运行列表失败,则直接返回失败
if (processes == null || processes.isEmpty()) {
...
return IPluginManager.PROCESS_AUTO;
}
//根据优先级分配坑位进程
int prevMatchPriority = -1; //临时变量,保存上一个ProcessRecord的进程分配优先级
ProcessRecord selectRecord = null; //被选中的坑位进程
for (ProcessRecord r : STUB_PROCESSES) {
synchronized (r) {
if (r.calculateMatchPriority(plugin) > prevMatchPriority) {
prevMatchPriority = r.calculateMatchPriority(plugin);
selectRecord = r;
} else if (r.calculateMatchPriority(plugin) == prevMatchPriority) {
if (r.mobified < selectRecord.mobified) {
selectRecord = r;
}
}
}
}
if (selectRecord == null) { //不应该出现
return IPluginManager.PROCESS_AUTO;
}
synchronized (selectRecord){
//插件已在分配进程中运行,直接返回
if (selectRecord.calculateMatchPriority(plugin) == Integer.MAX_VALUE && (selectRecord.state == StubProcessState.STATE_ALLOCATED || selectRecord.state == StubProcessState.STATE_RUNNING))
{
return selectRecord.index;
}
selectRecord.resetAllocate(plugin, processes);
return selectRecord.index;
}
}
看下启动逻辑的proxyStartPluginProcess方法,将信息写入ProcessPitProviderBase这个ContentProvider中,借助ContentProvider实现多进程
static final boolean proxyStartPluginProcess(Context context, int index) {
//
ContentValues values = new ContentValues();
values.put(KEY_METHOD, METHOD_START_PROCESS);
values.put(KEY_COOKIE, PMF.sPluginMgr.mLocalCookie);
Uri uri = context.getContentResolver().insert(ProcessPitProviderBase.buildUri(index), values);
...
return true;
}
4 广播注册接收源码分析
看Loader类的loadDex方法里的regReceivers,这个方法最终通过mPluginHost.regReceiver完成广播接收器注册,他是在PmHostSvc类的regReceiver方法实现
public void regReceiver(String plugin, Map rcvFilMap) throws RemoteException {
PluginInfo pi = MP.getPlugin(plugin, false);
...
HashMap<String, List<IntentFilter>> receiverFilterMap = (HashMap<String, List<IntentFilter>>) rcvFilMap;
// 遍历此插件中所有静态声明的 Receiver
for (HashMap.Entry<String, List<IntentFilter>> entry : receiverFilterMap.entrySet()) {
if (mReceiverProxy == null) {
mReceiverProxy = new PluginReceiverProxy();//创建的是代理
mReceiverProxy.setActionPluginMap(mActionPluginComponents);
}
/* 保存 action-plugin-receiver 的关系 */
String receiver = entry.getKey();
List<IntentFilter> filters = entry.getValue();
if (filters != null) {
for (IntentFilter filter : filters) {
int actionCount = filter.countActions();
while (actionCount >= 1) {
saveAction(filter.getAction(actionCount - 1), plugin, receiver);
actionCount--;
}
// 注册 Receiver
mContext.registerReceiver(mReceiverProxy, filter);
}
}
}
}
看下PluginReceiverProxy
public void onReceive(Context context, Intent intent) {
...
// 根据 action 取得 map<plugin, List<receiver>>
HashMap<String, List<String>> pc = mActionPluginComponents.get(action);
if (pc != null) {
// 遍历每一个插件
for (HashMap.Entry<String, List<String>> entry : pc.entrySet()) {
String plugin = entry.getKey();
if (entry.getValue() == null) {
continue;
}
// 拷贝数据,防止多线程问题
List<String> receivers = new ArrayList<>(entry.getValue());
// 此插件所有声明的 receiver
for (String receiver : receivers) {
try {
// 在对应进程接收广播, 如果进程未启动,则拉起之
int process = getProcessOfReceiver(plugin, receiver);
if (process == IPluginManager.PROCESS_PERSIST) {
IPluginHost host = PluginProcessMain.getPluginHost();
host.onReceive(plugin, receiver, intent);
} else {
IPluginClient client = MP.startPluginProcess(plugin, process, new PluginBinderInfo(PluginBinderInfo.NONE_REQUEST));
client.onReceive(plugin, receiver, intent);
}
} ...
}
看下client.onReceive的实现,最终调用的PluginReceiverHelper.onPluginReceiverReceived
public static void onPluginReceiverReceived(final String plugin,
final String receiverName,
final HashMap<String, BroadcastReceiver> receivers,
final Intent intent) {
...
// 使用插件的 Context 对象
final Context pContext = Factory.queryPluginContext(plugin);
if (pContext == null) {
return;
}
String key = String.format("%s-%s", plugin, receiverName);
BroadcastReceiver receiver = null;
if (receivers == null || !receivers.containsKey(key)) {
try {
// 使用插件的 ClassLoader 加载 BroadcastReceiver
Class c = loadClassSafety(pContext.getClassLoader(), receiverName);
if (c != null) {
receiver = (BroadcastReceiver) c.newInstance();
if (receivers != null) {
receivers.put(key, receiver);
}
...
} else {
receiver = receivers.get(key);
}
if (receiver != null) {
final BroadcastReceiver finalReceiver = receiver;
// 转到 ui 线程
Tasks.post2UI(new Runnable() {
@Override
public void run() {
finalReceiver.onReceive(pContext, intent);
}
});
}
}
proxy先接收到系统的广播,然后再由PluginReceiverProxy转发到对应插件中的receiver调用它的onReceive
5 启动插件ContentProvider源码分析
看PluginProviderClient类,比如其中的insert方法
/**
* 调用插件里的Provider
* @see android.content.ContentResolver#insert(Uri, ContentValues)
*/
public static Uri insert(Context c, Uri uri, ContentValues values) {
Uri turi = toCalledUri(c, uri);
return c.getContentResolver().insert(turi, values);
}
toCalledUri方法
/**
* 将从插件里的URI转化成系统传过来的URI。可自由指定在哪个进程启动。例如:
*
* @param context 当前的Context对象,目前暂无用
* @param plugin 要使用的插件
* @param uri URI对象
* @param process 进程信息,若为PROCESS_AUTO,则根据插件Manifest来指定进程
* @return 转换后可直接在ContentResolver使用的URI
*/
public static Uri toCalledUri(Context context, String plugin, Uri uri, int process) {
...
if (uri.getAuthority().startsWith(PluginPitProviderBase.AUTHORITY_PREFIX)) {
// 自己已填好了要使用的插件名(以PluginUIProvider及其它为开头),这里不做处理
return uri;
}
// content://com.qihoo360.mobilesafe.PluginUIP
if (process == IPluginManager.PROCESS_AUTO) {
// 直接从插件的Manifest中获取
process = getProcessByAuthority(plugin, uri.getAuthority());
if (process == PROCESS_UNKNOWN) {
// 可能不是插件里的,而是主程序的,直接返回Uri即可
return uri;
}
}
String au;//分配权限
if (process == IPluginManager.PROCESS_PERSIST) {
au = PluginPitProviderPersist.AUTHORITY;
} else if (PluginProcessHost.isCustomPluginProcess(process)) {
au = PluginProcessHost.PROCESS_AUTHORITY_MAP.get(process);
} else {
au = PluginPitProviderUI.AUTHORITY;
}
String newUri = String.format("content://%s/%s/%s", au, plugin, uri.toString().replace("content://", ""));
return Uri.parse(newUri);
}
去PluginPitProviderBase类中看下uri的转化,还看insert方法,重点是看PluginProviderHelper.getProvider方法
public ContentProvider getProvider(PluginUri pu) {
...
String auth = pu.transferredUri.getAuthority();
// 已有缓存?直接返回!
ContentProvider cp = mProviderAuthorityMap.get(auth);
if (cp != null) {
return cp;
}
// 开始构建插件里的ContentProvider对象
cp = installProvider(pu, auth);
...
// 加入列表。下次直接读缓存
mProviderAuthorityMap.put(auth, cp);
...
return cp
}
看下installProvider方法
private ContentProvider installProvider(PluginUri pu, String auth) {
// 开始尝试获取插件的ProviderInfo
ComponentList col = Factory.queryPluginComponentList(pu.plugin);
...
ProviderInfo pi = col.getProviderByAuthority(auth);
...
// 通过ProviderInfo创建ContentProvider对象
Context plgc = Factory.queryPluginContext(pu.plugin);
ClassLoader cl = plgc.getClassLoader();
ContentProvider cp;
try {
cp = (ContentProvider) cl.loadClass(pi.name).newInstance();//依然是通过ClassLoader创建ContentProvider实例
} ...
// 调用attachInfo方法(内部会调用onCreate)
try {
cp.attachInfo(plgc, pi);
} catch (Throwable e) {
// 有两种可能:
// 1、第三方ROM修改了ContentProvider.attachInfo的实现
// 2、开发者自己覆写了attachInfo方法,其中有Bug
// 故暂时先Try-Catch,这样若插件的Provider没有使用Context对象,则也不会出现问题
return null;
}
return cp;
}