Android插件化方案

插件化技术涉及得非常广泛,其中最核心的就是Android的类加载机制和反射机制。下图载自腾讯bugly:

插件化发展历史

插件化技术最初源于免安装运行apk的想法,这个免安装的apk可以理解为插件。支持插件化的app可以在运行时加载和运行插件,这样便可以将app中一些不常用的功能模块做成插件,一方面减小了安装包的大小,另一方面可以实现app功能的动态扩展。想要实现插件化,主要是解决下面三个问题:

  • 插件中代码的加载和与主工程的互相调用
  • 插件中资源的加载和与主工程的互相访问
  • 四大组件生命周期的管理

下面是比较出名的几个开源的插件化框架,按照出现的时间排序。研究它们的实现原理,可以大致看出插件化技术的发展,根据实现原理可以将这几个框架划分成了三代。

第一代:dynamic-load-apk最早使用ProxyActivity这种静态代理技术,由ProxyActivity去控制插件中PluginActivity的生命周期。该种方式缺点明显,插件中的activity必须继承PluginActivity,开发时要小心处理context。而DroidPlugin通过Hook系统服务的方式启动插件中的Activity,使得开发插件的过程和开发普通的app没有什么区别,但是由于hook过多系统服务,异常复杂且不够稳定。
第二代:为了同时达到插件开发的低侵入性(像开发普通app一样开发插件)和框架的稳定性,在实现原理上都是趋近于选择尽量少的hook,并通过在manifest中预埋一些组件实现对四大组件的插件化。另外各个框架根据其设计思想都做了不同程度的扩展,其中Small更是做成了一个跨平台,组件化的开发框架。
第三代:VirtualApp比较厉害,能够完全模拟app的运行环境,能够实现app的免安装运行和双开技术。Atlas是阿里开源出来的一个结合组件化和热修复技术的一个app基础框架,其广泛的应用与阿里系的各个app,其号称是一个容器化框架。

Atlas/VirtualApk/RePlugin对比

Atlas是伴随着手机淘宝的不断发展而衍生出来的一个运行于Android系统上的一个容器化框架,我们也叫动态组件化(Dynamic Bundle)框架。它主要提供了解耦化、组件化、动态性的支持。覆盖了工程师的工程编码期、Apk运行期以及后续运维期的各种问题。

VirtualAPK是滴滴出行自研的一款优秀的插件化框架,功能完备。支持几乎所有的Android特性;四大组件均不需要在宿主manifest中预注册,每个组件都有完整的生命周期,入侵性极低。

RePlugin是一套完整的、稳定的、适合全面使用的,占坑类插件化方案,由360手机卫士的RePlugin Team研发,也是业内首个提出”全面插件化“(全面特性、全面兼容、全面使用)的方案。

Atlas:https://github.com/alibaba/atlas/tree/master/atlas-docs
VirtualAPK:https://github.com/didi/VirtualAPK/wiki
RePlugin:https://github.com/Qihoo360/RePlugin/wiki

一、定义

从定义上说,Atlas定义为组件化,而VirtualAPK和RePlugin则定义为插件化。这两种还是有一点点不同的,组件化偏重于编译期,插件化偏重于运行期。换句话来说,Atlas在编译的时候是需要把bundle放在一起处理的,而后面两个则可以完全独立开,和开发新的一个apk一样,最后打包的时候配置一下就行。

二、接入难度

初次接入Atlas,那真的会有点懵逼,官方文档已经万年没更新的,GitHub上面的demo和文档上的好多都不一样,就算接入了,也会出现一堆问题,简直想死。VirtualAPK,滴滴这个,接入还算简单的了,文档也比较完善。RePlugin,360这个也挺简单的,宿主和插件分得很清楚。所以接入难度:RePlugin最快,VirtualAPK其次,Atlas最麻烦。

三、功能

三者都有的功能是远程bundle,按需加载。意思是项目中某个模块,打包的时候不打进apk,等你安装了,需要用到的时候再下载那个模块进行加载显示,从而减少apk的安装体积。Atlas会把插件打成so的形式,而VirtualAPK和RePlugin会打成apk的形式,都是放在内存卡,然后调用各自的安装加载方法就行。Atlas安装后,可以把so删掉,但VirtualAPK的apk需要一直在内存卡,否则是打不开的,而RePlugin安装后会自动备份一个apk到缓存目录。除了远程bundle功能,Atlas还有热修复的功能,可以不升级apk就实现宿主和组件的更新。

四、更新插件方式

Atlas更新插件的话,必须要和宿主一起,打差异补丁才能更新,而VirtualAPK和RePlugin是可以直接通过下载一个新的插件apk,然后调安装方法就能实现插件的更新。

五、插件独立性

Atlas和宿主的依赖还是挺多,毕竟官方也强调是组件化,不是插件化。而VirtualAPK,它可以是一个独立的app,但插件里面也定义和宿主的关联,就是说这个插件apk并不能给其他宿主用,只能给插件里面声明的那个宿主使用。RePlugin呢,就比较独立了,里面不用声明和宿主的联系,所以你生成一个插件后,这个插件可以给其他宿主调用。

六、宿主和插件的公共库

如果宿主和插件都用到一些公共依赖库,比如http库,图片加载库,这个时候怎么处理?Atlas处理就简单了,毕竟是组件化,和宿主的项目都是在一起。可以在项目里面定义一个middleLibrary,这个库里面依赖一些公共的库或者资源,然后宿主和插件都依赖他就行了。VirtualAPK呢,由于插件里面要声明和宿主的关联,所以他会自动检测,如果插件中依赖的库,在宿主里面也有的话,他会自动去掉一个,不会重复。RePlugin是比较独立的,没有和宿主太多联系,所以目前大家的做法是宿主compile,插件 provided的形式,而共用资源的话,RePlugin是不提倡的了。

总结

如果你的app需要热更新和插件的功能,推荐使用Atlas;如果你的app仅用到插件,在需要的时候才下载加载的话,你可以选择VirtualAPK和RePlugin;如果你的插件希望其他宿主也能用的话,那就只能RePlugin了,RePlugin就像一个应用市场,你的宿主仅仅是一个壳,然后把需要的插件下载加载使用就行,更新的话也无需更新宿主,直接更新插件就行。

Atlas

Atlas是伴随着手机淘宝的不断发展而衍生出来的一个运行于Android系统上的一个容器化框架,我们也叫动态组件化(Dynamic Bundle)框架。它主要提供了解耦化、组件化、动态性的支持。覆盖了工程师的工程编码期、Apk运行期以及后续运维期的各种问题。

包结构

其整体包结构和正常Apk包结构类似。区别在于armeabi中存放大量的so,每个so都是APK转过来的,作为一个单独的bundle。

架构

这一块是Atlas的整体设计,分为五层:

  • 第一层我们称之为Hack层,包括OS Hack toolkit & verifier,这里我们对系统能力做一些扩展,然后做一些安全校验。
  • 第二层是Bundle Franework,就是我们的容器基础框架,提供Bundle管理、加载、生命周期、安全等一些最基本的能力。
  • 第三层是运行期管理层,包括清单,我们会把所有的Bundle和它们的能力列在一个清单上,在调用时方便查找;另外是版本管理,会对所有Bundle的版本进行管理;再就是代理,这里就是和业界一些插件化框架机制类似的地方,我们会代理系统的运行环境,让Bundle运行在我们的容器框架上;然后还有调试和监控工具,是为了方便工程期开发调试。
  • 第四层是业务层了,这里我们向业务方暴露了一些接口,如框架生命周期、配置文件、工具库等等。
  • 最上面一层是应用接入层,就是我们的业务代码了。

技术细节

1、Manifest与依赖
Bundle的Manifest在编译期会进行Merge,Bundle的依赖会单独Merge,因为涉及依赖仲裁最终输出BundleInfoList

2、多ClassLoader
为什么要用多ClassLoader,我的猜想是,类卸载。
JVM提供的类加载器始终不会释放,因此根据可达性,其加载出来的类,也始终不会释放,但是用户自定义的就可以。
这保证了一个组件在使用完以后,不存在任何实例化对象,任何类对象,保证了性能与稳定。

DelegateClassLoader先查找宿主Bundle的PathClassLoader,然后根据BundleInfoList,查找对应Bundle的BundleClassLoader

3、资源

用DelegeteResources替换系统的Resource,Bundle的资源在运行期会添加到AssertsPath中
并且进行分区,防止资源错乱
并且根据ART、Dalvik适配以及机型适配
为了防止资源名冲突,在资源名前后添加bundle独有id

4、按需加载

在想用一个组件的时候,到BundleInfoList中查找对应的Bundle,进行加载
每个组件都有生命周期管理,这样保证组件在不用的时候资源可以释放

5、动态化

主Bundle基于ClassLoader实现,业务Bundle基于差量Merge
可以结合Andfix,它基于Native Hook实现,用于方法的动态修改

问题

为什么atlas这么好,支付宝却开始弃用了呢?诱因是android p的发布,大家可以看看这篇了解下 https://blog.csdn.net/tyro_smallnew/article/details/80468034 (Android P阻止调用非sdk api后,Atlas该何去何从)。

android p开始android开始禁止开发者使用非官方api,也就是禁止反射使用android不想让开发者使用的类和属性,但是atlas的工作原理就是反射这些不让用的api。

然而android p并不是主要原因,主要原因是 Atlas并不是刚需而且有些限制:
1、基于动态化加载的方案有许多,前端技术已经开始应用到app开发中,weex rn性能也已经达到开发者预期,前端方案明显是比atlas更有优势,而在支付宝中你会发现大部分都是前端页面
2、插件更新其实并不常用,一般大厂的应用都通过严谨的测试,出问题的概率比较小,大部分逻辑都是服务端来实现,可控性很强,所以插件更新在一些公司没有用武之地
3、Atlas依赖管理比较复杂,插件之间存在依赖也可能是多级依赖,如果要跨插件依赖需要整理好依赖树,但需求常改,之前的依赖树可能不符合需求需要重整,这个时候非常耗时耗力,而原生开发的依赖管理就方便得多
4、兼容问题,Atlas经历了好几代android的兼容:5.0时代的art虚拟机兼容、7.0浏览器资源加载兼容、各个版本的api兼容。。。你会在atlas核心代码中发现很多if else来判断android版本,不过atlas做的不错兼容的也很好,但每次android出版本都要兼容一次确实费时费力

Atlas support android Q

Atlas 5.1.0.9-rc26 已发布,此次更新较为重大,官方提醒升级请慎重。

主要更新内容如下:

  • 支持 Android Q,弃用 atlasupdate 项目
  • bundle 需要在运行时打包在 maindex 中
  • 未来将不再支持动态部署
  • dexpatch 仅在 Android P 以下的版本受支持,并且 Atlas 不会在 Android P 及更高版本上加载补丁(新补丁将在稍后提供)
  • 弃用 DelegateClassloader、DelegateResources、BundleClassloader、InstrumentationHook 等
  • 如果想要在使用这个 bundle 之前初始化一个 bundle,可以使用:BundleIniter.initBundle(String bundleName,null)
  • 不支持 bundle 依赖项,因为所有 bundle 都在运行时使用 PathClassloader
  • 之后将不支持远程视图和远程片段,因为所有 bundle 都在运行时使用 PathClassloader
  • AtlasDemo 已更新

Replugin

Replugin通过Hook住系统的PathClassLoader并重写了loadClass方法来实现拦截类的加载过程,并且每一个插件apk都设置了一个PluginDexClassLoader,在加载类的时候先使用这个PluginDexClassLoader去加载,加载到了直接返回否则再通过持有系统或者说是宿主原有的PathClassLoader去加载,这样就保证了不管是插件类、宿主类、还是系统类都可以被加载到。

那么Replugin这么做的思想是什么?其实我觉得是破坏了ClassLoader的双亲委派模型,或者说叫打破这种模型,为什么这样说?首先双亲委派模型是层层向上委托的树形加载,而Replugin在收到类加载请求时直接先使用了插件ClassLoader来尝试加载,这样的加载模式应该算是网状加载,所以说Replugin是通过Hook系统ClassLoader来做到破坏了ClassLoader的双亲委派模型,Replugin将所有插件apk封装成一个Plugin对象统一在插件管理进程中管理,而每一个插件apk都有属于自己的ClassLoader,在类被加载的时候首先会使用插件自己的ClassLoader去尝试加载,这样做的好处是,可以精确的加载到需要的那个类,而如果使用双亲委派只要找到一个同路径的类就返回,那么这个被返回的类有可能并不是我们需要的那个类。

参考资料

https://www.jianshu.com/p/ceded2da7847
《Android插件化技术——原理篇》

atlas:
1.https://github.com/alibaba/atlas
2.https://blog.csdn.net/qq_36523667/article/details/99178542
3.https://mp.weixin.qq.com/s/G0dsrVYytT8WdJ6U90NPiA
4.https://www.jianshu.com/p/d3d881a59561
5.开源Android容器化框架Atlas开发者指南