从工程领域来看,模块化、组件化、插件化三种技术都是指将复杂代码进行拆分,达到解偶分层、便于管理的目的。广泛意义上,将代码按照业务模块划分就是模块化,若是再进一步从模块化代码中抽出通用于全部App的组件,做为一个独立的module或者maven依赖(好比一些比较有名的第三方SDK),这个组件生成的过程就叫组件化。插件化则是指将App按必定规则拆分红几个若干个APK,除了主APK,其余APK都可以经过网络下发而后经过主APK加载。经过加载、修改、卸载非主APK,必定程度上给予了APP热修复的功能。然而随着Android 9.0上私有API的限制,插件化受到了极大的限制,主流方案慢慢向稳定、务实的的组件化方案演进。java
比较传统的一些架构是利用MVC、MVP、MVVM对项目进行分包,然而随着项目代码量愈来愈多,修改的时候会牵一发而动全身,并且不利于并行开发和回归测试。经过将一些通用的代码进行拆分,而后使用maven依赖进来,能够减小本地的代码量并解偶了一部分维护工做。随着业务的日渐复杂,aar级别的组件化还不够,每次修改部分业务都会须要对其余相关业务进行进行回归测试,一些当前版本不会用到的代码仍然会打包进APk,这样不利于后续的维护。因此业界相继提出了“组件化架构”的思想,也就是在整个Project层面对代码按照模业务块、功能模块、基础模块进行划分,而后在具体的Module中用MVC、MVP、MVVM等思想构建具体的代码。以下图所示:android
其中基础组件层主要包括:网络库、日志库、路由库等。但在实际的开发过程当中,每每须要对这些基础库封装一层,好比对网络库用观察者模式封装一层来实现UI移步加载、路由须要自定义拦截器等,这一层就是咱们的service层,也就是功能组件层。Service层的主要目的是向外提供服务,而业务组件则是具体的业务逻辑。具体的分包示意图以下:bash
为了不循环依赖和业务逻辑之间的交叉,同一层的组件是不能直接相互引用的。 这是由于: 1)除了主Module,其余任何一个业务组件均可能是处于加载或者不加载的状态,好比有2个模块A和B,若是相互依赖,且假设没有加载B,而线上模块A使用了B中功能,那么A可能会crash; 2)任何一个业务组件都是独立的,也就是说多是由不一样的部门并行开发的,不该该互相依赖。 鉴于这两个规则,同一层之间是不能直接通讯(若是只考虑第1点,不考虑独立依赖,可使用反射,可是不够美观且会影响性能)。 这个通讯包括两方面: 1)界面之间的相互跳转; 2)服务之间及业务之间的相互调用。同时组件如何注册、加载、卸载,这些都是组件化架构须要解决的。网络
结合上述的理论基础,在实践过程当中须要解决的技术难点主要有:模块间的通讯、路由表的自动维护、组件的生命周期管理、主包管理及进程间通讯等。架构
说到通讯,咱们能想到的方案有两种,路由和事件总线。路由能够解决界面的跳转和一些dialog、toast的显隐,可是不能解决服务之间的相互调用和回调。业界提出了相似于Android中四大组件之一ServiceManager的处理方法--“接口下沉”,也就是在基础组件层新建一个ServiceManager,并提供通用服务接口IService,在须要暴露服务的地方实现该接口并手动/自动注册到ServiceManager中,这样任何须要该服务的地方均可以经过:ServiceManager.get(Classclz)静态工厂方法取得,相似于ServiceManager中的addService(String name, IBinder service)和IBinder getService(String name)方法,固然ServiceManager类彻底能够由注解来生成。第二种方案就是使用事件总线,好比EventBus或者RxBus,由于事件总线自己是经过观察者模式实现的同时能够支持跳转,因此也能够用来替代路由+“接口下沉”的方案。app
在实践过程当中,现有的方案都须要维护3个HashMap,分别是路由表、服务表以及组件表,当服务多到必定的程度,手动维护3个哈希表是一场灾难。以小赢理财现有的Scheme路由库为例,初代版本中是在内存中维护一个静态的HashMap路由表,key表示路径,value表明calss。虽然这样方便省事可是可维护性较差,一旦跳转规则变化或者忘记及时更新则会失效。秉承着“能让机器完成毫不本身动手”的原则,在迭代中改为了经过反射去扫描AndroidManifest,自动生成维护HashMap,这样能够作到map的自动注册。可是因为扫描是在Application的onCreare()方法中完成,用AOP测试发现扫描过程比较耗时,这种自动化的方式是以牺牲启动时间为代价的。联想到注解,能够经过编译时注解插入代码动态生成HashMap,但尝试事后发现部分场景下行不通,由于编译时注解的特性只在源码编译时生效,没法扫描到aar包里的注解,这种状况不适合远程预埋aar,动态下发的场景。运行时注解也会不可避免的会形成性能的缺失。幸运的是,Android官方提供了Transform API,能够用来在.class转换为.dex前操做class文件。这样配合ASM(若是以为javap命令生成字节码太麻烦,可使用IntelliJ IDEA插件‘Bytecode outline’,可方便快捷根据java类生成字节码)或javassist就能够动态的修改字节码,从而动态生成HashMap而不须要损耗性能。这种方式配合后台下发的方式,就能保住灵活性。框架
大体步骤以下:异步
1.新建buildSrc工程,或者独立工程;maven
2.接入Transform依赖:implementation 'com.android.tools.build:gradle:3.2.1',值得注意的是这个包里面包含了ASM库;ide
3.新建一个class实现Transform类,将扫描范围设置为:TransformManager.SCOPEFULLPROJECT,并在tranform(TransformInvocation transformInvocation)中遍历目录输入和jar输入,并使用ClassVisitor操做字节码;
4.最后在自定义Plugin中注册这个自定义Transform。
具体的操做细节能够参考官方文档。
一个进程对应着一个虚拟机,虚拟机须要管理APk的生命周期。同理,若是把APP看做一个虚拟机,把各个业务组件当作是小型的APP,那么APP是须要妥善管理各个业务组件的生命周期的。也就是说咱们须要同步资源初始化、使用、销毁的时机。能够模拟虚拟机的工做流程:加载-验证-准备-解析-初始化-使用-卸载,同时使用ApplicationDelegate代理,hook住Application的各个生命周期,这样就能够实现组件的同步加载,须要主动销毁时,则能够将module手动卸载。
随着拆分出来的Module和aar愈来愈多,每次都须要重复配置依赖和项目基本参数。因为每层(主Module层、业务组件层、功能组件层、基础组件层)之间的依赖都大同小异的,所以抽出一层gradle_component,专门用来配置通用gradle,这样就能够统一依赖,好比:
而后把这些gradle分别apply到对应的层级,固然为了方便管理减小.gradle文件,能够将具体的依赖经过自定Plugin的形式注入。因为module和aar比较多,当真正进行build的时候,须要检查settings.gradle并对每一个Module进行初始化配置,再运行具体的task进行打包。这个操做随着Module个数的增多,执行的耗时会直线上升。实际状况是,假设只修改了某个module,并只想运行这一部分增量代码,这个时候能够经过切换主APP完成。因为Android项目是经过plugin来识别的:
apply plugin:'com.android.application' // 主module
apply plugin: 'com.android.library' // module
复制代码
这样咱们能够在本地自定义一个'isMainAPP'参数来控制主Module和Module的切换。
if(isMainAPP) { // 切换
apply plugin:'com.android.application' // 主module
} else {
apply plugin: 'com.android.library' // module
}
复制代码
进程是最小的资源分配管理单位,当业务组件多大必定的程度时,会须要考虑使用多进程通讯。若是只是简单的跳转不涉及到数据的获取,那么路由组件是能够胜任的,由于Android内置的Intent机制原本就是跨进程的。若是须要同步数据,则须要考虑进程通讯中出现的脏数据,好比同时操做sharepreferences是比较棘手的,由于sharepreferences在文件和内存中各有一份数据,且有时候不相同。Android系统提供了基于mmap的Binder通讯机制,落实到工程代码就是实现AIDL,生成远程Binder类和当前进程的Proxy类,并定义相关的Service和BinderPool。可是这样稍微有点重,能够考虑使用现有的ContentProvider + SQLite(ContentProvider自己也是AIDL通讯机制,只是系统对其进行了一层封装),在路由库中增长对多进程通讯拦截链的支持。
当前的一些大公司都前后开源了本身的组件化架构框架,比较知名的有美团的modular-event,阿里的ARouter以及获得(逻辑思惟主打APP)的DDComponentForAndroid。其设计思想大同小异,基本也是机遇以上的设计要点,辅助一些同步和异步功能。美团舍弃了以前开发的“WMRouter”路由,转而使用了modular-event,阿里和获得则是使用路由+接口下沉的方式去构建整个架构。“组件化架构”可以清晰的划分项目结构,严格的将代码根据“业务组件”、“模块组件”、“基础组件”进行划分,各个项目组成员能够并行开发module而互不干扰,并且其可扩展性也比较强,对业务不断扩大的项目是一个不错的选择。
整理的一些相关架构及资料(关注主页)
喜欢文章的点个赞鼓励一下叭~