1、VirtualAPK的接入
1.1、宿主工程引入VirtualApk
- 在项目Project的build.gradle中添加依赖
dependencies {
classpath 'com.didi.virtualapk:gradle:0.9.8.6'
}
- 在宿主app的build.gradle中引入VirtualApk的host插件
apply plugin: 'com.didi.virtualapk.host'
- 在app中添加依赖
dependencies {
implementation 'com.didi.virtualapk:core:0.9.8'
}
- 在Application中完成PluginManager的初始化
public class VirtualApplication extends Application {
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
PluginManager.getInstance(base).init();
}
}
1.2、配置插件Apk
- 在插件project中配置
classpath 'com.didi.virtualapk:gradle:0.9.8.6'
- 在插件app的build.gradle中引入plugin插件
apply plugin: 'com.didi.virtualapk.plugin'
- 配置插件信息和版本
virtualApk{
// 插件资源表中的packageId,需要确保不同插件有不同的packageId
// 范围 0x1f - 0x7f
packageId = 0x6f
// 宿主工程application模块的路径,插件的构建需要依赖这个路径
// targetHost可以设置绝对路径或相对路径
targetHost = '../../../VirtualAPkDemo/app'
// 默认为true,如果插件有引用宿主的类,那么这个选项可以使得插件和宿主保持混淆一致
//这个标志会在加载插件时起作用
applyHostMapping = true
}
- 设置签名(Virtual仅支持Release,host项目和plugin项目签名一致)
signingConfigs {
release {
storeFile file('/Users/wuliangliang/AndroidSubjectStudyProject/PluginProject/VirtualAPkDemo/keystore/keystore')
storePassword '123456'
keyAlias = 'key'
keyPassword '123456'
}
}
buildTypes {
release {
signingConfig signingConfigs.release
}
}
1.3、执行生成插件Plugin
-
执行assemablePlugin 产生Plugin文件
-
将插件Plugin安装到手机中
adb push ./app/build/outputs/plugin/release/com.alex.kotlin.virtualplugin_20190729172001.apk /sdcard/plugin_test.apk
- 在插件Plugin项目中所有的四大组件的使用都和原生使用方法一致
- 注意问题
- 要先构建一次宿主app,才可以构建plugin,否则异常
- 插件布局文件中要设置资源的ID,否则异常:Cannot get property ‘id’ on null object
- plugin 增加 gradle.properties 文件并配置android.useDexArchive=false,否则异常
1.4、在宿主程序中使用插件Plugin
- 在宿主App中加载插件apk
private void loadApk() {
File apkFile = new File(Environment.getExternalStorageDirectory(), "Test.apk");
if (apkFile.exists()) {
try {
PluginManager.getInstance(this).loadPlugin(apkFile);
} catch (Exception e) {
e.printStackTrace();
}
}
}
在插件下载或安装到设备后,获取插件的文件,调用PluginManager.loadPlugin()加载插件,PluginManager会完成所有的代码解析和资源加载,详细内容后面的源码分析;
2. 执行界面跳转至插件中
final String pkg = "com.alex.kotlin.virtualplugin”; //插件Plugin的包名
Intent intent = new Intent();
intent.setClassName(pkg, "com.alex.kotlin.virtualplugin.MainPluginActivity”); //目标Activity的全路径
startActivity(intent);
2、Virtual APK 源码分析
2.1、PluginManager初始化
- 在Application中添加VirtualApk初始化
PluginManager.getInstance(base).init();
- PluginManager.getInstance(base):创建PluginManager实例,单例对外提供
public static PluginManager getInstance(Context base) {
if (sInstance == null) {
synchronized (PluginManager.class) {
if (sInstance == null) {
sInstance = createInstance(base); // 调用createInstance()方法创建PluginManager,单例对外提供
}
}
}
return sInstance;
}
- createInstance(base):创建PluginManager对象
private static PluginManager createInstance(Context context) {
try {
//1、获取metaData
Bundle metaData = context.getPackageManager()
.getApplicationInfo(context.getPackageName(), PackageManager.GET_META_DATA)
.metaData;
if (metaData == null) {
return new PluginManager(context); //2、
}
String factoryClass = metaData.getString("VA_FACTORY”); //3、此处获取的是什么?
if (factoryClass == null) {
return new PluginManager(context);
}
//4、创建PluginManager
PluginManager pluginManager = Reflector.on(factoryClass).method("create", Context.class).call(context);
if (pluginManager != null) {
return pluginManager;
}
} catch (Exception e) {
Log.w(TAG, "Created the instance error!", e);
}
return new PluginManager(context);
}
createInstance()执行逻辑:
- 从宿主Context中解析APk中的metaData
- 如果metaData为null,则直接使用context创建PluginManager
- 从metaData中获取factoryClass,并反射创建PluginManager,否则直接创建PluginManager
- PluginManager的构造函数
protected PluginManager(Context context) {
if (context instanceof Application) {
// 1、
this.mApplication = (Application) context;
this.mContext = mApplication.getBaseContext();
} else {
final Context app = context.getApplicationContext();
if (app == null) {
this.mContext = context;
this.mApplication = ActivityThread.currentApplication();
} else {
this.mApplication = (Application) app;
this.mContext = mApplication.getBaseContext();
}
}
mComponentsHandler = createComponentsHandler(); //2、
hookCurrentProcess(); //3、
}
PluginManager()执行流程:
- 根据context类型,分别进行获取并赋值mApplication & mContext
- 创建ComponentsHandler对象,ComponentsHandler的作用是作为工具类,用于处理Intent和Service服务,关于ComponentsHandler后面会再提到
- Hook Instrumentation和SystemServices(主要使用Hook技术)
- hookCurrentProcess()
protected void hookCurrentProcess() {
hookInstrumentationAndHandler();
hookSystemServices();
}
- hookInstrumentationAndHandler():Hook Activity启动中使用的Instrumentation和Handler的CallBack,关于Handler的Callback查看Android消息队列
ActivityThread activityThread = ActivityThread.currentActivityThread();
Instrumentation baseInstrumentation = activityThread.getInstrumentation(); //1、
final VAInstrumentation instrumentation = createInstrumentation(baseInstrumentation); //2、
//3、
Reflector.with(activityThread).field("mInstrumentation").set(instrumentation);
//4、
Handler mainHandler = Reflector.with(activityThread).method("getHandler").call();
Reflector.with(mainHandler).field("mCallback").set(instrumentation);
this.mInstrumentation = instrumentation; // 赋值 PluginManager的mInstrumentation
执行流程:
- 从ActivityThread中获取的系统中的Instrumentation对象
- 创建代理的Instrumentation对象,内部保存原对象
- 反射设置代理的VAInstrumentation对象到ActivityThread中
- 同样Hook替换 ActivityThread 中的类H(Handler)中的mCallback,拦截 handleMessage()的回调逻辑
- hookSystemServices():Hook系统的IActivityManager服务
Singleton<IActivityManager> defaultSingleton;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
//1、
//8.0 以上获取IActivityManagerSingleton,采用AIDL获取AMS
defaultSingleton = Reflector.on(ActivityManager.class).field("IActivityManagerSingleton").get();
} else {
//8.0 之前获取gDefault,使用代理执行AMS
defaultSingleton = Reflector.on(ActivityManagerNative.class).field("gDefault").get();
}
IActivityManager origin = defaultSingleton.get(); //2
IActivityManager activityManagerProxy = (IActivityManager) Proxy.newProxyInstance(mContext.getClassLoader(), new Class[]{
IActivityManager.class},createActivityManagerProxy(origin));//3
Reflector.with(defaultSingleton).field("mInstance").set(activityManagerProxy); // 4、
if (defaultSingleton.get() == activityManagerProxy) {
this.mActivityManager = activityManagerProxy;//5、
}
hook系统服务在Hook 技术 文章中已经接收过程了,这里简单介绍逻辑:
- 根据Android版本的处理方式不同 ,8.0 以上获取IActivityManagerSingleton,采用AIDL获取AMS,8.0 之前获取gDefault,使用代理执行AMS
- 反射获取系统中原类的IActivityManager的代理类;
- 动态代理IActivityManager接口,此处创建的是ActivityManagerProxy对象;
- 反射设置代理的IActivityManager实例到系统中;
- 获取设置的代理的Proxy,保存在mActivityManager中;
由四大组件启动过程源码分析,Hook了Instrumentation对象就可以完成对Activity的创建过程的修改,Hook了系统的IActivityManager可以实现对AMS工作的拦截,而AMS对四大组件的整个工作过程至关重要,也就是说我们已经掌控了四大组件;
2.2、加载Plugin插件
- loadPlugin()
public void loadPlugin(File apk) throws Exception {
//1、
LoadedPlugin plugin = createLoadedPlugin(apk);
//2、
this.mPlugins.put(plugin.getPackageName(), plugin);
}
loadPlugin()中主要完成两件事:
- 根据传入的apk,创建LoadedPlugin对象
- 在mPlugins中根据包名保存创建的对象,使用时直接根据插件的包名获取
- LoadedPlugin:在构造函数中完成了大量的数据操作,具体细节见注释,下面逐步分析VirtualAPK是如何加载插件的
public LoadedPlugin(PluginManager pluginManager, Context context, File apk) throws Exception {
//1、保存安装包的APk路径、pluginManager、context
this.mPluginManager = pluginManager;
this.mHostContext = context;
this.mLocation = apk.getAbsolutePath();
//2、解析Plugin的AndroidManifest.xml文件
this.mPackage = PackageParserCompat.parsePackage(context, apk, PackageParser.PARSE_MUST_BE_APK);
this.mPackage.applicationInfo.metaData = this.mPackage.mAppMetaData;
//3、创建并实例化PackageInfo对象
this.mPackageInfo = new PackageInfo();
this.mPackageInfo.applicationInfo = this.mPackage.applicationInfo;
this.mPackageInfo.applicationInfo.sourceDir = apk.getAbsolutePath();
if (Build.VERSION.SDK_INT >= 28
|| (Build.VERSION.SDK_INT == 27 && Build.VERSION.PREVIEW_SDK_INT != 0)) {
try {
this.mPackageInfo.signatures = this.mPackage.mSigningDetails.signatures;
} catch (Throwable e) {
PackageInfo info = context.getPackageManager().getPackageInfo(context.getPackageName(), PackageManager.GET_SIGNATURES);
this.mPackageInfo.signatures = info.signatures;
}
} else {
this.mPackageInfo.signatures = this.mPackage.mSignatures;
}
//保存包名
this.mPackageInfo.packageName = this.mPackage.packageName;
this.mPackageInfo.versionCode = this.mPackage.mVersionCode;
this.mPackageInfo.versionName = this.mPackage.mVersionName;
this.mPackageInfo.permissions = new PermissionInfo[0];
//4、创建插件中自己的PackManager
this.mPackageManager = createPluginPackageManager();
//5、创建插件中自己的PluginContext
this.mPluginContext = createPluginContext(null);
this.mNativeLibDir = getDir(context, Constants.NATIVE_DIR);
//获取so文件路径
this.mPackage.applicationInfo.nativeLibraryDir = this