2、为什么要有组件化?
Android项目中代码量达到一定程度,编译将是一件非常痛苦的事情,短则一两分钟,长则达到五六分钟。随着app业务的壮大,模块越来越多,代码量超10万是很正常的,这个时候我们会遇到以下问题:
- 稍微改动一个模块的一点代码都要编译整个工程,耗时耗力
- 公共资源、业务、模块混在一起耦合度太高
- 不方便测试
3、组件化开发的好处(优点):
组件化开发可以有效降低代码模块的耦合度,使代码架构更加清晰,同时模块化的编译可以有效减少编译时间,当然总的编译时间是不会减少的,只是App模块化之后开发某个模块时,只需要编译特定模块,可以快速编译调试。
- 业务模块分开,每个模块可以独立开发编译运行,解耦的同时也降低了项目的复杂度。
- 开发单个模块时可以共享资源和工具类。
- 可以针对单个模块测试, 开发调试时不需要对整个项目进行编译。
- 多人合作时可以只关注自己的业务模块,把某一业务当成单一项目来开发。
- 可以灵活的对业务模块进行组装和拆分。
4、组件化开发的主要思路
就是将一个Module拆分成若干个Module,由主App提供统一的入口,每个拆分后的Module都依赖共享的Common依赖库,通过相关配置,各个Module可以独立运行调试,也可以供主App依赖使用。
5、组件化开发的步骤:
- 新建一个lib组件,New → Module → Andorid Library,取名BaseUtilLib,我们将所有的公共的工具类、网络封装等类放在其中。
- 新建一个lib组件,New → Module → Andorid Library,取名BaseReslLib,我们将所有的公共资源、drawable、String等类放在其中。
- 将app按照需求划分成多个模块,比如按业务按地区等都可以,然后分别创建Module模块。
- 逐一开发某个模块,每个组件都可以引用[1][2]步骤的BaseUtilLib、BaseReslLib里的公共类和公共资源,组件可以单独开发、单独测试。
- 将工程中的开发好的组件模块,引入到工程的app中,然后就可以发布了。
6、举一个更简单的图例:
app:壳工程
module1:组件1
module2:组件2
common:第三库,公用工具、自定义 View、主题等
二、插件化
1、为什么需要插件化?
我们在项目的开发过程中,难免会遇到产品需求的变更或者出现bug。在传统的模式中,我们首先需要修改代码,然后重新打包Apk,再交给公司的运营去官网或者应用商店上线,用户在打开应用的时候就会进行更新了。但是这种模式有几个缺点:
- 上线周期长,从修改代码到用户更新需要较长的时间;
- 用户更新代价较大,每次用户更新都需要下载整个Apk包,整个Apk包括了一个应用的所有代码,要消耗用户较多的流量。
- 如果是一些重要的更新,为了确保用户都能更新到,还需要强制更新,即用户打开App后如果不更新则退出应用,这种对用户来说是极其不友好的。
- 还有另外一种情况,某些较大的App功能很多,比如支付宝、微信等,如果将这些功能全部塞到一个Apk中,那将会是一个巨型Apk,用户在安装或者更新Apk时将会经过漫长的等待时间。
基于以上几点,Android的插件化技术应运而生**,**插件化技术可以将整个巨型Apk按照功能模块划分,不同的功能打包成不同的Apk,然后应用的主Apk按需加载对应功能的Apk,用户只需要安装应用的主Apk即可。主Apk相当于一个壳,它会按需加载其他功能模块的Apk。通过这种模式,不仅解决了巨型Apk的问题,而且当某个功能模块需要变化时,也只需要修改对应功能的代码,打包功能Apk并更新即可,这样不仅可以让用户及时更新,而且更新的代价也很小。但是,我们知道,在Android中,没有安装的apk是不能直接运行的,那么要想实现插件化,我们就必须让宿主Apk能够加载功能Apk并运行。
2、插件化的好处:
- 宿主和插件分开编译
- 并发开发,宿主和插件都是apk,开发是互不影响的,只需要宿主给插件一个上下文
- 动态更新插件,不需要安装,下载之后就可以直接打开
- 按需下载模块
- 可以解决方法数或变量数爆棚问题
3、插件化的标准:
是不是每个app都能运行在我们的宿主app里面呢?答案肯定不是。我们必须在宿主app里面设计一套标准,让插件app满足我们这个标准才能够运行。最主要的是插件app因为没有安装,所以是没有上下文的,就需要我们的宿主将上下文传过去。插件化的activity也没有生命周期,所以我们的宿主也要对插件的生命周期进行管理,以及资源、layout等。
4、插件化技术的发展
大概从2012年开始,前前后后出现了很多插件化/组件化的框架,各种框架都有自己的特色。
AndroidDynamicLoader:2012年7月,基于Fragement实现,通过动态加载不同的Fragement来替换界面,首次使用addAssetPath来读取插件中的资源。
Altas技术沙龙分享:2014年初,阿里的一次技术沙龙分享,主要讲淘宝Alta这项技术的概念,并没有涉及详细实现,当时已经应用于淘宝客户端,未开源。
android-pluginmgr:2014年7月,主要通过操作java虚拟机字节码,通过动态生成一个插件类的子类如四大组件(需要在manifest中提前声明),达到插件化的功能(最新的已经改成hook系统的一些对象如Instrumentation、ActivityThread等)
dynamic-load-apk:2014年底,该框架使用代理的方式实现,并没有hook系统的底层对象、方法。比较有代表性,后文中会详细分析。
OpenAltas:2015年4月,后来改名为ACDD。该框架参考了淘宝App的很多经验,主要就是Hook的思想(据说是反编译代码),同时,还首次提出来通过扩展AAPT(android assert packaging tool)来解决插件与宿主的资源id冲突的问题。
Android-Plugin-Framework:2015年5月,采用hook系统底层很多方法,感觉是把能hook的对象基本都hook住了,功能相对完整,但不支持so插件,兼容性不好,目前在实际应用的实践比较少。
DroidPlugin:2015年8月,它也hook了很多Android系统的底层代码,包括本地的PMS、AMS、ActivityThread、Instrumentation、H等对象,能把任意的App都加载到宿主里运行。比较有代表性,后文也会详细分析。
DynamicAPK:2015年10月,参考了之前提到的ACDD的设计方案,并通过扩展aapt解决资源问题。
Small:2015年底,是一个比较轻巧的跨平台插件化框架,比DroidPlugin更加轻量一点,基本原理也是对底层的对象进行hook,并使用gradle脚本来解决资源冲突的问题,可以通过配置url来支持插件跳转。
TwsPluginFramework,2017年初,同样hook系统底层对象方法,并通过appt解决资源冲突的问题。
Atlas:2017年3月,它是一个动态组件化的方案,也hook了很多底层对象方法,支持so,支持增量补丁和热修复,是个单进程的框架。