3.1 基本原理
- 合并宿主和插件的ClassLoader
需要注意的是,插件中的类不可以和宿主重复 - 合并插件和宿主的资源
重设插件资源的packageId,将插件资源和宿主资源合并 - 去除插件包对宿主的引用
构建时通过Gradle插件去除插件对宿主的代码以及资源的引用
3.2 四大组件的实现原理
- Activity
采用宿主manifest中占坑的方式来绕过系统校验,然后再加载真正的activity; - Service
动态代理AMS,拦截service相关的请求,将其中转给Service Runtime
去处理,Service Runtime
会接管系统的所有操作; - Receiver
将插件中静态注册的receiver重新注册一遍; - ContentProvider
动态代理IContentProvider,拦截provider相关的请求,将其中转给Provider Runtime
去处理,Provider Runtime
会接管系统的所有操作。
VirtualAPK的整体架构图,更详细的内容请大家阅读源码。
3.3 VirtualAPK的整体架构图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0QiMH5DA-1630587542054)(https://user-gold-cdn.xitu.io/2017/10/19/303e5cfd242442fee27eb48da71fe5a5?imageView2/0/w/1280/h/960/ignore-error/1)]
3.4 原理分析源码
鸿洋大神写的源码分析(滴滴官网都推荐看鸿洋大神写的)
滴滴插件化方案 VirtualApk 源码解析
下面这位大神就不知道是谁了(滴滴官网推荐看的)
VirtualAPK 资源加载机制分析
4 使用
4.1 插件和宿主必须compile相同的aar
比如宿主compile了如下aar
compile 'com.didi.virtualapk:core:0.9.0'//滴滴VirtualAPK的依赖
compile 'com.alibaba:fastjson:1.2.39'
compile'com.github.CymChad:BaseRecyclerViewAdapterHelper:2.9.30'
插件工程需要访问宿主sdk中的类和资源,那么可以在插件工程中同样compile sdk的aar,如下:
compile 'com.didi.virtualapk:core:0.9.0'//滴滴VirtualAPK的依赖
compile 'com.alibaba:fastjson:1.2.39'
compile'com.github.CymChad:BaseRecyclerViewAdapterHelper:2.9.30'
这样一来,插件工程就可以正常地引用sdk了。并且,插件构建的时候会自动将这个aar从apk中剔除
4.2 宿主工程
- 在工程根目录下
build.gradle
中添加
dependencies {
classpath 'com.didi.virtualapk:gradle:0.9.0'
}
- 在App的
build.gradle
中顶部添加
apply plugin: 'com.didi.virtualapk.host'
- 在App的
build.gradle
中compile
添加
dependencies {
compile 'com.didi.virtualapk:core:0.9.0'
}
- 编写MyApp继承Application重写attachBaseContext方法中初始化插件引擎(别忘了在AndroidManifest.xml配置Application)
@Override
protected void attachBaseContext(Context context) {
super.attachBaseContext(context);
PluginManager.getInstance(context).init();
}
- 推荐大家在Application启动的时候去加载插件,不然的话,请注意插件的加载时机。 考虑一种情况,如果在一个较晚的时机去加载插件并且去访问插件中的资源,请注意当前的Context。比如在宿主Activity(MainActivity)中去加载插件,接着在MainActivity去访问插件中的资源(比如Fragment),需要做一下显示的hook,否则部分4.x的手机会出现资源找不到的情况。
PluginManager pluginManager = PluginManager.getInstance(this);
//此处是当查看插件apk是否存在,如果存在就去加载(比如修改线上的bug,把插件apk下载到sdcard的根目录下取名为Demo.apk)
File apk = new File(Environment.getExternalStorageDirectory(), "Demo.apk");
if (apk.exists()) {
try {
pluginManager.loadPlugin(apk);
} catch (Exception e) {
e.printStackTrace();
}
}
- AndroidManifest.xml添加读写SD卡的权限,如果插件apk是从网上获得的,还需要添加上网权限(我只是简单的测试,直接把插件apk放到sd卡中)
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
4.3 插件工程
- 在工程根目录下
build.gradle
中添加
dependencies {
classpath 'com.didi.virtualapk:gradle:0.9.0'
}
- 在App的
build.gradle
中顶部添加依赖以及插件配置信息
apply plugin: 'com.didi.virtualapk.plugin'//注意这个是plugin结尾,宿主是以host结尾的
// 插件配置信息,放在文件最下面
virtualApk {
// 插件资源表中的packageId,需要确保不同插件有不同的packageId.
packageId = 0x6f
// 宿主工程application模块的路径,插件的构建需要依赖这个路径,我这个宿主工程和插件工程在同一级目录下,所以下面这样写
targetHost = '../VirtualAPKHostDemo/app'
//默认为true,如果插件有引用宿主的类,那么这个选项可以使得插件和宿主保持混淆一致
applyHostMapping = true
[]( )4.5 Gradle版本推荐
------------------------------------------------------------
目前VirtualAPK构建器还在持续完善之中,因此插件构建推荐使用Gradle 2.14.1版本,3.x版本可能有适配问题,我们正在努力去兼容各种Gradle版本。同时,待VirtualAPK构建器完善之后,其代码也将开源,敬请期待。
----------------------------
目前VirtualAPK构建器还在持续完善之中,因此插件构建推荐使用Gradle 2.14.1版本,3.x版本可能有适配问题,我们正在努力去兼容各种Gradle版本。同时,待VirtualAPK构建器完善之后,其代码也将开源,敬请期待。