一、概述
文章看过千百遍,不如源码走一遍。
一则,提升阅读源码的能力;
二则,在阅读源码的过程中学会思考,理解其实现原理;
如是,便有了此文。
1.1 相关技术点
- 设计模式 — 动态代理模式
- 源码分析 — Activity的清单注册校验
- 源码分析 — ActivityThread(二)之ActivityThread的浅析
- 源码分析 — Activity的启动流程
- 源码分析 — PackageManagerService(一)之启动流程
- 源码分析 — PackageManagerService(二)之resolveIntent()
- 源码分析 — Binder机制(一)(进程间通信)
- 源码分析 — Binder机制(二)之IActivityManager
1.2 参考
1.3 版本
- Android Framework:
Api 23
- VirtualAPK:
com.didi.virtualapk:core 0.9.0
二、插件框架初始化
2.1 时序图
2.2 框架初始化的源码分析
在Application中开始初始化插件框架:PluginManager.getInstance(application).init()
// PluginManager.class
public static PluginManager getInstance(Context base) {
if (sInstance == null) {
synchronized (PluginManager.class) {
if (sInstance == null)
// 首次进来要初始化
sInstance = new PluginManager(base);
}
}
return sInstance;
}
// PluginManager.class
private PluginManager(Context context) {
Context app = context.getApplicationContext();
if (app == null) {
this.mContext = context;
} else {
this.mContext = ((Application)app).getBaseContext();
}
// 这里初始化是核心
prepare();
}
// PluginManager.class
private void prepare() {
Systems.sHostContext = getHostContext();
//利用Hook技术,替换ActivityThread中的Instrumentation对象
this.hookInstrumentationAndHandler();
//利用Hook技术,替换ActivityManagerNative中的gDefault对象中的IActivityManager对象
this.hookSystemServices();
}
// PluginManager.class
private void hookInstrumentationAndHandler() {
try {
Instrumentation baseInstrumentation = ReflectUtil.getInstrumentation(this.mContext);
if (baseInstrumentation.getClass().getName().contains("lbe")) {
// reject executing in paralell space, for example, lbe.
System.exit(0);
}
/*
* 创建Instrumentation的代理类VAInstrumentation;
* (将Instrumentation作为参数传入,这样可以在调用系统逻辑之前进行预处理)
*/
final VAInstrumentation instrumentation = new VAInstrumentation(this, baseInstrumentation);
Object activityThread = ReflectUtil.getActivityThread(this.mContext);
ReflectUtil.setInstrumentation(activityThread, instrumentation);
ReflectUtil.setHandlerCallback(this.mContext, instrumentation);
this.mInstrumentation = instrumentation;
} catch (Exception e) {
e.printStackTrace();
}
}
// PluginManager.class
private void hookSystemServices() {
try {
/*
* 通过反射获取持有 ActivityManagerProxy(AMP) 对象的单例对象 Singleton ;
* (这部分可以看:[源码分析 — Binder机制(二)之IActivityManager]
* (https://blog.csdn.net/love667767/article/details/79653077))
*/
Singleton<IActivityManager> defaultSingleton = (Singleton<IActivityManager>)
ReflectUtil.getField(ActivityManagerNative.class, null, "gDefault");
/*
* 自己创建一个AMP代理类,将系统原有的AMP作为一个参数传入;
* 好处:
* 在调用系统AMP之前会先调用我们自己创建的AMP,然后进行一些预处理,
* 最后调用系统的AMP,我们的代理类,其实质就是系统AMP的代理);
*/
IActivityManager activityManagerProxy =
ActivityManagerProxy.newInstance(this, defaultSingleton.get());
/*
* Hook IActivityManager from ActivityManagerNative
* 这里创建了自己的 AMP 类后,当然要将它设置回 Singleton 单例了,
* 这样子我们自己实现的 AMP 才能生效;
*/
ReflectUtil.setField(defaultSingleton.getClass().getSuperclass(),
defaultSingleton, "mInstance", activityManagerProxy);
if (defaultSingleton.get() == activityManagerProxy) {
this.mActivityManager = activityManagerProxy;
}
} catch (Exception e) {
e.printStackTrace();
}
}
三、插件的加载
3.1 加载插件的示例代码
在底座(宿主)Apk中加载插件的代码片段:
// 加载plugin.apk插件包
private void loadPlugin() {
PluginManager pluginManager = PluginManager.getInstance(this);
// 指明被加载的插件所在路径
File apk = new File(getExternalStorageDirectory(), "app-release.apk");
if (apk.exists()) {
try {
// 加载插件Apk
pluginManager.loadPlugin(apk);
} catch (Exception e) {
e.printStackTrace();
}
}
}
3.2 插件初始化源码分析
/**
* PluginManager.class
*
* 加载插件Apk包,然后调用插件Apk的Application;
* 注意:加载的文件必须以.apk为后缀;
*/
public void loadPlugin(File apk) throws Exception {
if (null == apk) {
throw new IllegalArgumentException("error : apk is null.");
}
if (!apk.exists()) {
throw new FileNotFoundException(apk.getAbsolutePath());
}
// 1.加载插件Apk,然后解析其文件结构
LoadedPlugin plugin = LoadedPlugin.create(this, this.mContext, apk);
if (null != plugin) {
this.mPlugins.put(plugin.getPackageName(), plugin);
// 2.调用插件里面的Application(插件内的Application已经被插件框架托管)
plugin.invokeApplication();
} else {
throw new RuntimeException("Can't load plugin which is invalid: " + apk.getAbsolutePath());
}
}
// LoadedPlugin.class
public static LoadedPlugin create(PluginManager pluginManager, Context host, File apk) throws Exception {
return new LoadedPlugin(pluginManager, host, apk);
}
LoadedPlugin(PluginManager pluginManager, Context context, File apk)
throws PackageParser.PackageParserException {
this.mPluginManager = pluginManager;
this.mHostContext = context;
this.mLocation = apk.getAbsolutePath();
/*
* 1.关键:通过系统的PackageParser.parsePackage()方法去解析Apk文件;
* 参考:[PackageManagerService(一)之启动流程](https://blog.csdn.net/love667767/article/details/79595237)
*
* 说明:由于系统版本的适配问题,这里框架做了一层封装,根据版本的不同做了不同的处理;
*/
this.mPackage = PackageParserCompat.parsePackage(context, apk, PackageParser.PARSE_MUST_BE_APK);
this.mPackage.applicationInfo.metaData = this.mPackage.mAppMetaData;
this.mPackageInfo = new PackageInfo();
this.mPackageInfo.applicationInfo = this.mPackage.applicationInfo;
// ...
// 插件包的管理对象
this.mPackageManager = new PluginPackageManager();
// 插件包的Context
this.mPluginContext = new PluginContext(this);
this.mNativeLibDir = context.getDir(Constants.NATIVE_DIR, Context.MODE_PRIVATE);
// 资源获取
this.mResources = createResources(context, apk);
this.mAssets = this.mResources.getAssets();
// 创建插件的ClassLoader
this.mClassLoader = createClassLoader(context, apk, this.mNativeLibDir, context.getClassLoader());
// 将插件中Lib库的依赖包拷贝到底座(宿主)中;
tryToCopyNativeLib(apk);
/*
* 2.下面这部分跟PackageManagerService(PMS)逻辑类似,就是将插件清单文件解析出来的信息存入到
* mInstrumentationInfos、mActivityInfos、mServiceInfos、mProviderInfos、mReceiverInfos字段中;
*/
// Cache instrumentations
Map<ComponentName, InstrumentationInfo> instrumentations = new HashMap<ComponentName, InstrumentationInfo>();
for (PackageParser.Instrumentation instrumentation : this.mPackage.instrumentation) {
instrumentations.put(instrumentation.getComponentName(), instrumentation.info);
}
this.mInstrumentationInfos = Collections.unmodifiableMap(instrumentations);
this.mPackageInfo.instrumentation = instrumentations.values().toArray(new InstrumentationInfo[instrumentations.size()]);
// Cache activities
Map<ComponentName, ActivityInfo> activityInfos = new HashMap<ComponentName, ActivityInfo>();
for (PackageParser.Activity activity : this.mPackage.activities) {
activityInfos.put(activity.getComponentName(), activity.info);
}
this.mActivityInfos = Collections.unmodifiableMap(activityInfos);
this.mPackageInfo.activities = activityInfos.values().toArray(new ActivityInfo[activityInfos.size()]);
// Cache services
Map<ComponentName, ServiceInfo> serviceInfos = new HashMap<ComponentName, ServiceInfo>();
for (PackageParser.Service service : this.mPackage.services) {
serviceInfos.put(service.getComponentName(), service.info);
}
this.mServiceInfos = Collections.unmodifiableMap(serviceInfos);
this.mPackageInfo.services = serviceInfos.values().toArray(new ServiceInfo[serviceInfos.size()]);
// Cache providers
Map<String, ProviderInfo> providers = new HashMap<String, ProviderInfo>();
Map<ComponentName, ProviderInfo> providerInfos = new HashMap<ComponentName, ProviderInfo>();
for (PackageParser.Provider provider : this.mPackage.providers) {
providers.put(provider.info.authority, provider.info);
providerInfos.put(provider.getComponentName(), provider.info);
}
this.mProviders = Collections.unmodifiableMap(providers);
this.mProviderInfos = Collections.unmodifiableMap(providerInfos);
this.mPackageInfo.providers = providerInfos.values().toArray(new ProviderInfo[providerInfos.size()]);
// Register broadcast receivers dynamically
Map<ComponentName, ActivityInfo> receivers = new HashMap<ComponentName, ActivityInfo>();
for (PackageParser.Activity receiver : this.mPackage.receivers) {
receivers.put(receiver.getComponentName(), receiver.info);
try {
BroadcastReceiver br = BroadcastReceiver.class.cast(getClassLoader().loadClass(receiver.getComponentName().getClassName()).newInstance());
for (PackageParser.ActivityIntentInfo aii : receiver.intents) {
// 这里将插件清单文件中的静态广播动态注册到底座(宿主)中;
this.mHostContext.registerReceiver(br, aii);
}
} catch (Exception e) {
e.printStackTrace();
}
}
this.mReceiverInfos = Collections.unmodifiableMap(receivers);
this.mPackageInfo.receivers = receivers.values().toArray(new ActivityInfo[receivers.size()]);
}