Android插件化技术调研

历览千载书,时时见遗烈。这篇文章主要讲述Android插件化技术调研相关的知识,希望能为你提供帮助。
一、技术背景              android的插件化技术,目前已经比较成熟,微信、淘宝、携程、360手机助手中都应用到了插件化。插件化技术的特点是无需单独安装apk,即可运行,即插即用,无需升级宿主应用,减少app的更新频率,
除此之外他还可以降低模块耦合,按需加载,节省流量等特点。
 
二、已有框架技术对比            表1从是否支持四大组件、是否须在主manifest预注册等多个维度对主流开源框架进行对比,从而筛选出比较符合项目的框架有VirtualAPK、RePlugin。
h2.western { font-family: "Liberation Sans", sans-serif; font-size: 16pt } h2.cjk { font-family: "Noto Sans CJK SC Regular"; font-size: 16pt } h2.ctl { font-size: 16pt } p { margin-bottom: 0.25cm; line-height: 120% } a:link { }表1主流开源框架的对比

特性
DynamicLoadApk
DynamicAPK
Small
DroidPlugin
VirtualAPK
【Android插件化技术调研】RePlugin
ACDD/Atlas
支持四大组件
只支持Activity
只支持Activity
只支持Activity
全支持
全支持
全支持
全支持
组件无需在宿主manifest中预注册
?
×
?
?
?
?
×
插件可以依赖宿主
?
?
?
×
?
轻度依赖
?
支持PendingIntent
×
×
×
?
?
?
未明确
Android特性支持
大部分
大部分
大部分
几乎全部
几乎全部
几乎全部
未明确
兼容性适配
一般
一般
中等



未明确
插件构建

部署aapt
Gradle插件

Gradle插件


Gradle插件
部署aapt
框架轻重






相对轻量
相对轻量
相对轻量
重量
支持安卓版本








API Level 15+
API Level 9+


接入难度











侧重阶段






运行期
运行期
运行期
编译期
热修复能力







插件安装后可否删除








不能
不能
可以
插件更新方式








插件独立更新
插件独立更新
插件及宿主须同时更新


              对表1各插件化框架进行筛选,最后可得出VirtualAPK为最优候选框架。
              以下对重点对VirtualAPK做详细调研。
 
td p { margin-bottom: 0cm } p { margin-bottom: 0.25cm; line-height: 120% } a:link { } 三、VirtualAPK3.1 VirtualAPK主要优势
  • 功能完备: 支持几乎所有的Android特性, 四大组件均不需要在宿主manifest中预注册,每个组件都有完整的生命周期;
  • 优秀的兼容性: 兼容市面上几乎所有的Android手机,资源方面适配小米、Vivo、Nubia等,对未知机型采用自适应适配方案, 极少的Binder Hook且hook过程做了充分的兼容性适配,插件运行逻辑和宿主隔离,确保框架的任何问题都不会影响宿主的正常运行。
  • 入侵性极低: 四大组件无需继承特定的基类,插件可以依赖宿主中的代码和资源,通过Gradle插件来完成插件的构建,整个过程对开发者透明。
          如下是VirtualAPK的整体架构图。
h2.western { font-family: "Liberation Sans", sans-serif; font-size: 16pt } h2.cjk { font-family: "Noto Sans CJK SC Regular"; font-size: 16pt } h2.ctl { font-size: 16pt } p { margin-bottom: 0.25cm; line-height: 120% } a:link { } 
Android插件化技术调研

文章图片

          详情参见:
          https://github.com/didi/VirtualAPK/wiki


3.2 VirtualAPK主要工作原理
          VirtualAPK 对于插件没有额外的约束,原生的 apk 即可作为一个插件。插件工程编译生成 apk 后,通过宿主 App 加载,每个插件 apk 被加载后,都会在宿主中创建一个单独的 LoadedPlugin 对象。如上图所示,通过这些 LoadedPlugin 对象,VirtualAPK 就可以管理插件并赋予插件新的意义,使其可以像手机中安装过的App一样运行。
p { margin-bottom: 0.25cm; line-height: 120% } a:link { } code.cjk { font-family: "DejaVu Sans Mono", monospace } code.ctl { font-family: "Liberation Mono", monospace }
Android插件化技术调研

文章图片

图2 插件开发及加载过程
          图3描述了LoadedPlugin的结构,LoadedPlugin包含有插件apk中定义的activities、services、
          broadcast receivers、providers、classloader、resources等资源,LoadedPlugin实例化及加载时机如图2所示,由PluginManager执行其loadPlugin方法将插件apk加载进dalvik。


Android插件化技术调研

文章图片

图3 LoadedPlugin结构
3.2.1基本原理
  • 动态代理
                  基于JDK Proxy、InvocationHandler实现对任意java类的AOP控制。
 
  • Hook AMSInstrumentationIContentProvider
    Hook技术基于反射和动态代理技术。
    VirtualAPK通过Hook技术,从而实现对Activity、Service、ContentProvider的控制。
    • Hook AMS
      Hook要点为基于动态代理技术实现ActivityManagerProxy,以实现拦截ActivityManagerService的方法调用,然后利用反射将ActivityManagerNative实例替换掉ActivityManagerNative的gDefault成员。
p { margin-bottom: 0.25cm; line-height: 120% } a:link { }
Android插件化技术调研

文章图片

图4 AMS Hook代码
    • Hook Instrumentation
                                      Hook要点为扩展Instrumentation以实现对Activity启动过程的控制,然后使用反射将扩展
                                      Instrumentation实例替换ActivityThread对象原来的Instrumentation对象。
p { margin-bottom: 0.25cm; line-height: 120% } a:link { }
Android插件化技术调研

文章图片

图5 Instrumentation Hook代码



    • Hook IContentProvider
p { margin-bottom: 0.25cm; line-height: 120% } a:link { }
Android插件化技术调研

文章图片

  图6 IContentProvider Hook代码
 
  • 合并宿主和插件的ClassLoader
    dalvik.system.BaseDexClassLoader内部定义了DexPathList,用于记录去哪里加载指定的类,而DexPathList内部定义了dexElements,专门记录已加载的dex。
    对于宿主工程来说,在加载插件工程后,将插件dex插入到宿主工程的dexElements集合后面即可,在之后的运行中就可以按需加载插件中的class。
    需要注意的是,插件中的类不可以和宿主重复 。
p { margin-bottom: 0.25cm; line-height: 120% } a:link { }
Android插件化技术调研

文章图片

  图 7宿主和插件Dex合并示意图
  • 合并插件和宿主的资源 重设插件资源的packageId,将插件资源和宿主资源合并
  • 去除插件包对宿主的引用 构建时通过Gradle插件去除插件对宿主的代码以及资源的引用
3.2.2四大组件的实现原理
  • Activity
    采用宿主manifest中占坑的方式来绕过系统校验,然后再加载真正的activity;
    图8描述了VirtualAPK根据launchMode定义了2(standard) + 8(singleTop) + 8(singleTask) + 8(singleInstance) = 26个SubActivity坑.
p { margin-bottom: 0.25cm; line-height: 120% } a:link { }
Android插件化技术调研

文章图片
图 8 CoreLibrary工程的AndoidManifest
            图9描述了启动插件apk中activity的主要过程,也是VirtualAPK插件无需在AndroidManifest预注册的原理。该原理是VirtualAPK预先在AndroidManifest注册一些StubActivity,这些StubActivity并没有实际的代码实现,在之后启动插件Activity时,VirtualAPK将插件Activity改名为StubActivity,在绕过Android对启动插件Activity的合法校验后,再将StubActivity改回原来的Activity名称。
            该过程有两个关键点:
            1) 将插件Activity转换为StubActivity;
                    该阶段的作用是绕过Android对启动Activity的限制:Activity必须先在AndroidManifest注册,否则不能被startActivity。具体实现为利用hook的VAInstrumentation拦截Instrumentation的execStartActivity方法,并在该方法内将待启动的插件Activity名称改 为StubActivity。
            2) 将StubActivity还原为插件Activity。
                    此阶段的作用是继1)阶段绕过系统对插件Activity的合法校验后,将StubActivity名称改回原来的名称,以使得在Instrumentation.newActivity时实例并启动正确的目标Activity。
Android插件化技术调研

文章图片

p { margin-bottom: 0.25cm; line-height: 120% } a:link { }图9插件Activity启动过程
  • Service 动态代理AMS,拦截service相关的请求,将其中转给Service Runtime去处理,Service Runtime会接管系统的所有操作;
                  插件Service的启动/关闭基于被Hook的AMS,VirtualAPK在拦截到start/bind/stop/unbind Service的调用后,会执行ActivityManagerProxy的invoke方法,之后控制流程和数据会转交给ServiceDelegate对象,之后ServiceDeletegate会判断待启动的Service是本地服务还是远程服务,如果是本地服务,VirtualAPK会反射调用Service相应生命周期的方法;如果是远程服务,VirtualAPK先将目标插件apk加载进来,然后再反射调用目标Service的生命周期方法。
 
Android插件化技术调研

文章图片

图10插件Service启动过程
  • Receiver 将插件中静态注册的receiver重新注册一遍;
  • ContentProvider 动态代理IContentProvider,拦截provider相关的请求,将其中转给Provider Runtime去处理,Provider Runtime会接管系统的所有操作。
 
四、VirtualApk框架主要问题调研4.1 VirtualApk是否支持混淆
结论:VirtualApk主工程支持混淆,子工程也支持混淆

4.2、调研以下问题:
4.2.1 插件下载后,需不需要重启app才能完成加载?
结论:插件下载后不需要App重启即可生效,但插件升级后需重启app.
1) VirtualApk
github项目描述原话:
VirtualApk can dynamically load and run an APK file(we call it LoadedPlugin) seamless as an installed application.
说明插件下载后不需重启app即可生效运行;

2) 反证法思路:VirtualApk是当下最流行的插件化框架,如果其在插件下载后需要app重启,那么该框架肯定不会被业界认可;


3) 实践证实插件下载后不需app重启即可生效,但插件更新后需app重启方可生效。
尝试PluginManager卸载再装载组件的思路,实验结果未能成功自动更新升级后的组件。

4.3插件中和主app如果包含同样的库,执行中会不会遇到问题?
结论:所查阅文献明确说明 插件不能包含有主工程的代码,但插件工程在编译时应该可以使用主工程代码协助编译,只是编译完成后就不能有主工程代码。
经实际编码验证,如果插件和主app包含同样的代码,以主app代码为准。
参考文献:
https://blog.csdn.net/u012439416/article/details/76595643
https://juejin.im/entry/59e8618cf265da43143fcf68

4.4不用反射的方法,能不能让宿主直接调用插件中的类和方法?
结论:Java类经ClassLoader加载到JVM后,有反射和接口调用两种方式
Class A = Class.forName("com.some.class.A");
//反射方式
Method  method  =  A.getDeclaredMethod(...);
method.invoke(null,  ...);

//接口方式
InterfaceA objA = (InterfaceA) A.newInstance(...);
objA.interfaceMethod();
InterfaceA定义: 
interface  InterfaceA  {
void interfaceMethod();
}
h2.western { font-family: "Liberation Sans", sans-serif; font-size: 16pt } h2.cjk { font-family: "Noto Sans CJK SC Regular"; font-size: 16pt } h2.ctl { font-size: 16pt } pre.cjk { font-family: "DejaVu Sans Mono", monospace } pre.ctl { font-family: "Liberation Mono", monospace } p { margin-bottom: 0.25cm; line-height: 120% } a:link { } 五、VirtualAPK验证
5.1 宿主工程直接调用插件工程的四大组件 宿主工程调用插件工程的Activity/Service, 插件工程内部再调用自己或其他插件的四大组件 表2 VirtualAPK实验结果

方式
Activity
Service
BroadcastReceiver
ContentProvider
宿主工程直接调用插件工程

?
×
?
?
宿主工程间接调用插件工程

?
×
?
?
5.2 反射并使用插件工程的类 ? 在插件工程定义一工具类,然后在宿主工程Class.forName加载并使用该工具类,最后编译并打包运行验证效果,经实际验证该feature功能正常。5.3 混淆验证 ? 验证插件工程对宿主工程有单向依赖的情形。

表3 VirtualAPK实验结果

序号
宿主工程是否混淆

插件工程是否混淆

运行效果
1

混淆
混淆
?
2

混淆
未混淆
×
3

未混淆
混淆
?
4

未混淆
未混淆
?
6、总结                根据是否支持四大组件、是否需在宿主工程manifest预注册、插件工程是否可依赖宿主工程等指标,对表1各插件化框架进行筛选,最后可得出VirtualAPK为最优候选框架。但经过实际编码验证,四大组件除Service外都可被VirtualAPK支持。
7、参考文章
  • Android 360开源全面插件化框架RePlugin实战

https://blog.csdn.net/qiyei2009/article/details/78236520 工程github地址:https://github.com/Qihoo360/RePlugin

  • ACDD使用教程

http://www.jksoftcn.com/acddshi-yong-jiao-cheng.html

  • Android插件化框架

https://www.cnblogs.com/yoyohong/p/7599981.html

  • OpenAtlas之4四 资源分布结构解析

http://www.lxway.com/28562.htm

  • Android OpenAtlas初识

https://www.aliyun.com/jiaocheng/90603.html

  • Android插件化开发之OpenAtlas初体验
                      https://www.aliyun.com/jiaocheng/47101.html?spm=5176.100033.2.10.LuYTOz
  • Android插件化开发之OpenAtlas中四大组件与Application功能的验证https://blog.csdn.net/sbsujjbcy/article/details/47952269
  • Atlas、VirtualAPK、RePlugin三者的体验感受
                  https://www.jianshu.com/p/ceded2da7847
  • Android插件化:从入门到放弃
                  http://www.infoq.com/cn/articles/android-plug-ins-from-entry-to-give-up
h2.western { font-family: "Liberation Sans", sans-serif; font-size: 16pt } h2.cjk { font-family: "Noto Sans CJK SC Regular"; font-size: 16pt } h2.ctl { font-size: 16pt } pre.cjk { font-family: "DejaVu Sans Mono", monospace } pre.ctl { font-family: "Liberation Mono", monospace } td p { margin-bottom: 0cm } p { margin-bottom: 0.25cm; line-height: 120% } a:link { } 
p { margin-bottom: 0.25cm; line-height: 120% } a:link { } code.cjk { font-family: "DejaVu Sans Mono", monospace } code.ctl { font-family: "Liberation Mono", monospace } 
 
 
p { margin-bottom: 0.25cm; line-height: 120% } a:link { } code.cjk { font-family: "DejaVu Sans Mono", monospace } code.ctl { font-family: "Liberation Mono", monospace } 

























































    推荐阅读