目录
一、插件化和组件化区别
二、插件化的优势
1、 提高编译速度
2、 业务模块完全解耦
3、利于团队开发
4、动态更新,按需下载
5、 解决65535的限制
三、插件化实现原理
1、插件的类加载
2、插件的生命周期
3、应用的路由管理
4、插件的资源管理
5、插件的事件处理
四、插件化方案对比
五、结论
六、替代方案
1、自定义插件化方案
2、采用模块化或组件化方式
3、采用单独模块封装jar包或aar方式进行集成
一、插件化和组件化区别
组件化是将一个app拆分为多个模块进行协作开发,每个模块都是一个单独的组件,这些组件可以相互依赖,也可以单独调试运行。但是最终发布的时候,这些组件会合并在一起,组成一个整体的apk,这就是组件化开发。
插件化开发和组件化是有所不同的,插件化开发就是将一个app拆分成多个模块,但是每一个模块都是一个apk,最终打包的时候将宿主apk和插件apk分开打包,独立分发。宿主apk发布到市场,插件apk通过动态下发到手机存储空间,然后进行插装操作,宿主apk就能够加载到这个插件包,完成整个业务流程链路的闭合。
二、插件化的优势
1、 提高编译速度
开发过程中,每个插件都是独立编译运行的,会是当的提高我们的开发速度。
2、 业务模块完全解耦
每个业务模块都是完全独立的,可以随意删除和增加某一部分的功能。
3、利于团队开发
每个团队负责自己的功能,减少沟通的成本。
4、动态更新,按需下载
不需要重新安装即可实现功能的升级,对于一些不常用的功能,可以让用户按需下载,缩减整个程序包的大小。
5、 解决65535的限制
每个业务都是一个独立的apk,所以完全可以解决分包的问题。
三、插件化实现原理
通过上面的概述,可以知道插件化和组件化不同之处在于每一个插件都是一个可以独立安装运行的apk,在用户使用的过程中,只会安装宿主apk,其他的插件会通过网络等方式的下发到手机的内部存储中。所以想实现插件化,就必须将手机中的插件和宿主做关联,达到可以相互通信的效果,但是宿主模块和插件是不能直接进行相互通信的,下面我们会说明原因,所以我们需要一个中间件来作为媒介,这个中间件就是插件SDK。
所以在实现插件化的方案时,项目的结构搭建应该是分三部分:
宿主模块:app(主工程)
中间件:pluginSDK(插件化工具包)
插件模块:loginPlugin、registPlugin ……(拆分的业务插件)
因为宿主和插件模块只能做业务逻辑相关的功能,所以要想实现插件化,宿主必须通过pluginSDK来加载和调用插件,那么在pluginSDK中就必须解决以下几个问题:
插件的类加载
插件的生命周期
插件的路由管理
插件的资源管理
插件的事件处理
1、插件的类加载
如果想在主模块中去使用用插件,就必须将插件加载到主模块中,这里就会用到类加载器。我们知道Android中的虚拟机是Dalvik,它不是标准的Java虚拟机,所以在类加载机制上,和Java中的类加载器是有一些区别的。
Java虚拟机运行的class字节码,Android虚拟机运行的是dex字节码。
Android中使用的类加载器是PathClassLoader和DexClassLoader,PathClassLoader只能加载已经安装到手机的dex,而DexClassLoader可以加载未安装的dex,所以我们可以通过DexClassLoader加载器来加载插件apk,在pluginSDK的PluginManager类中来实现插件加载初始化的方法
2、插件的生命周期
上面提到宿主模块和插件是不能直接进行相互通信的,这是因为插件中的Activity虽然继承了Activity,但是它只能看作是一个普通的类,并不是真正意义上的Activity,因为它不具备Activity最经典的两个特性:【生命周期】和【上下文】。这是为什么呢?因为我们都知道,插件是直接通过宿主app的动态加载来使用的,插件本身并没有经过Android系统安装的过程,那也就是说,插件本身没有经过AMS的处理,Context对象就是在AMS的main函数中返回的,并且四大组件也是由AMS来统一调度的,所以说没有经过AMS的处理的Activity是没有“灵魂”的,那怎么办呢?我们可以通过“代理伪装”的方式来强制赋予插件Activity生命周期的能力。
3、应用的路由管理
插件加载完成,我们最常用的操作就是路由跳转,我们需要从宿主模块跳转到各个插件中去。如果用Intent的方式,那我们必须在宿主模块中的AndroidManifest中去注册插件中的Activity,这么做肯定是不合理的,如果引入第三方的路由框架,一个是违背了插件化框架设计的原则,另一个是会限制功能的开发,这显然也是不可取的。所以我们要在pluginSDK中创建一个ProxyActivity,让这个代理类来实现路由的跳转。
4、插件的资源管理
解决了上面的几个,其实就可以从宿主模块中实现跳转到插件中了,但是插件中的Activity并不能正常的进行UI显示和事件的监听,是因为插件不具备上下文对象,也就无法使用任何与上下文相关的api,就拿setContentView(R.layout.activity_login)这个类来说,他的本质实现应该是this.setContentView(R.layout.activity_ login),所以他也是需要上下文对象的,前面为了解决这个问题,我们已经将代理类ProxyActivity的实例传递给BaseActivity,而插件Activity又继承自这个BaseActivity,所以也就意味着插件Activity中的上下文对象是ProxyActivity代理类的上下文,那这里肯定是有问题的,插件类用代理类的上下文去获取资源肯定是不正确的,我们必须要在代理类中拿到插件类的资源并给到插件类才行。
5、插件的事件处理
通过上面的操作,我们已经可以从宿主模块跳转到对应的插件中了,但是如果在插件中的做一些与上下文相关的操作(如:findViewById),还是要调用BaseActivity中对应的方法,所以需要重写大量的Activity的方法来给子类做支持,如下图,that就是代理类传递给BaseActivity的上下文对象。这一系类的操作本质就是插件不具备上下文对象,我们只能强制给插件赋予这个属性。
四、插件化方案对比
框架 | Atlas | RePlugin | VirtualAPK | Small | DroidPlugin | DynamicAPK |
出处 | 阿里 | 360 | 滴滴 | 开源框架 | 360 | 携程 |
源码 地址 | https://github.com/alibaba/atlas/tree/master/atlas-docs | https://github.com/Qihoo360/RePlugin | https://github.com/didi/VirtualAPK | https://github.com/wequick/Small | https://github.com/Qihoo360/DroidPlugin/blob/master/readme_cn.md | https://github.com/nuptboyzhb/AndroidPluginFramework/tree/master/DynamicAPK_Project_Analysis |
定义 | Atlas是伴随着手机淘宝的不断发展而衍生出来的一个运行于Android系统上的一个容器化框架,我们也叫动态组件化(Dynamic Bundle)框架。 | RePlugin是一套完整的、稳定的、适合全面使用的,占坑类插件化方案。 | VirtualAPK是滴滴出行自研的一款优秀的插件化框架。 | 世界那么大,组件那么小。Small,做最轻巧的跨平台插件化框架。 | DroidPlugin 是360手机助手在Android系统上实现了一种新的插件机制:它可以在无需安装、修改的情况下运行APK文件,此机制对改进大型APP的架构,实现多团队协作开发具有一定的好处。 | 分别对不同的插件项目分配不同的packageId,然后对各个插件的资源进行编译,生成R文件,然后与宿主项目的R文件进行id的合并。 |
功能 | 1.远程bundle 2.插件以so形式,放在sdcard,加载后会自动删除。 3.热修复,可以不升级apk就实现宿主和组件的更新。 | 1.远程bundle 2.插件以apk形式放在sdcard,加载后还在会自动备份apk到缓存目录。 | 1.远程bundle 2.插件以apk形式放在sdcard,加载后还在,删除会打不开。 | 已过时不在调研 | 1.远程bundle 2.插件以apk形式放在sdcard,加载后还在会自动备份apk到缓存目录。 | 已过时不在调研 |
更新插件方式 | 必须要和宿主一起,打差异补丁才能更新。 | 直接通过下载一个新的插件apk,然后调安装方法就能实现插件的更新。 | 直接通过下载一个新的插件apk,然后调安装方法就能实现插件的更新。 | 已过时不在调研 | 直接通过下载一个新的插件apk,然后调安装方法就能实现插件的更新。 | 已过时不在调研 |
插件独立性 | 和宿主的依赖还是挺多,毕竟官方也强调是组件化,不是插件化。 | 插件不用声明和宿主的联系,所以你生成一个插件后,这个插件可以给其他宿主调用。 | 插件是一个独立的apk,但插件里面也定义和宿主的关联,就是说这个插件apk并不能给其他宿主用,只能给插件里面声明的那个宿主使用。 | 已过时不在调研 | 插件不用声明和宿主的联系,所以你生成一个插件后,这个插件可以给其他宿主调用。 | 已过时不在调研 |
Android特性 支持 | 几乎全部 | 几乎全部 | 几乎全部 | 大部分 | 几乎全部 | 大部分 |
支持四大组件 | 全支持 | 全支持 | 全支持 | 只支持Activity | 全支持 | 只支持Activity |
兼容性适配 | 中等 | 高 | 高 | 中等 | 中等 | 一般 |
Android API | 26(Android 8.0) | 28(Android 9.0) | 28(Android 9.0) | 25(Android 7.1) | 26(Android 8.0) | 25(Android 7.1) |
五、结论
VirtualAPK不支持对宿主进行热修复。
如果app需要热更新和插件的功能,推荐使用Atlas;
如果插件无法很好地从宿主中拆出来,或者插件的运行和宿主有较多的交互,推荐使用VirtualAPK;
如果同一个插件希望其他宿主也能用的话,那就只能RePlugin了,RePlugin就像一个应用市场,宿主仅仅是一个壳,然后把需要的插件下载加载使用就行,更新的话也无需更新宿主,直接更新插件就行。
六、替代方案
针对插件化无法适配高版本问题可采用以下方案:
1、自定义插件化方案
主工程Module为基础,然后其余可单独分开模块为插件,已Apk包的形式集成在主工程或者通过平台策略下发模式进行自定义下载安装
2、采用模块化或组件化方式
在基础功能项目内进行动态添加,通过API接口或者策略分发模式进行动态变更。
3、采用单独模块封装jar包或aar方式进行集成
在基础功能项目内进行动态添加,通过API接口或者策略分发模式进行动态变更 。