Android组件化架构实践,android系统软件开发

为了避免循环依赖和业务逻辑之间的交叉,同一层的组件是不能直接相互引用的。
这是因为:
1)除了主Module,其他任何一个业务组件都可能是处于加载或者不加载的状态,比如有2个模块A和B,如果相互依赖,且假设没有加载B,而线上模块A使用了B中功能,那么A可能会crash;
2)任何一个业务组件都是独立的,也就是说可能是由不同的部门并行开发的,不应该互相依赖。
鉴于这两个规则,同一层之间是不能直接通信(如果只考虑第1点,不考虑独立依赖,可以使用反射,但是不够美观且会影响性能)。
这个通信包括两方面:
1)界面之间的相互跳转;
2)服务之间及业务之间的相互调用。同时组件如何注册、加载、卸载,这些都是组件化架构需要解决的。

实践方案

结合上述的理论基础,在实践过程中需要解决的技术难点主要有:模块间的通信、路由表的自动维护、组件的生命周期管理、主包管理及进程间通信等。

1. 通信

说到通信,我们能想到的方案有两种,路由和事件总线。路由可以解决界面的跳转和一些dialog、toast的显隐,但是不能解决服务之间的相互调用和回调。业界提出了类似于Android中四大组件之一ServiceManager的处理方法–“接口下沉”,也就是在基础组件层新建一个ServiceManager,并提供通用服务接口IService,在需要暴露服务的地方实现该接口并手动/自动注册到ServiceManager中,这样任何需要该服务的地方都可以通过:ServiceManager.get(Classclz)静态工厂方法取得,类似于ServiceManager中的addService(String name, IBinder service)和IBinder getService(String name)方法,当然ServiceManager类完全可以由注解来生成。第二种方案就是使用事件总线,比如EventBus或者RxBus,因为事件总线本身是通过观察者模式实现的同时可以支持跳转,所以也可以用来替代路由+“接口下沉”的方案。

Android组件化架构实践,android系统软件开发_第1张图片

2. 自动注册

在实践过程中,现有的方案都需要维护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 Android组件化架构实践,android系统软件开发_第2张图片
outline’,可方便快捷根据java类生成字节码)或javassist就可以动态的修改字节码,从而动态生成HashMap而不需要损耗性能。这种方式配合后台下发的方式,就能保住灵活性。

Android组件化架构实践,android系统软件开发_第3张图片

大致步骤如下:

  1. 新建buildSrc工程,或者独立工程;

  2. 接入Transform依赖:implementation ‘com.android.tools.build:gradle:3.2.1’,值得注意的是这个包里面包含了ASM库;

  3. 新建一个class实现Transform类,将扫描范围设置为:TransformManager.SCOPEFULLPROJECT,并在tranform(TransformInvocation transformInvocation)中遍历目录输入和jar输入,并使用ClassVisitor操作字节码;

  4. 最后在自定义Plugin中注册这个自定义Transform。

具体的操作细节可以参考官方文档。

3. 组件生命周期管理

一个进程对应着一个虚拟机,虚拟机需要管理APk的生命周期。同理,如果把APP看作一个虚拟机,把各个业务组件看成是小型的APP,那么APP是需要妥善管理各个业务组件的生命周期的。也就是说我们需要同步资源初始化、使用、销毁的时机。可以模拟虚拟机的工作流程:加载-验证-准备-解析-初始化-使用-卸载,同时使用ApplicationDelegate代理,hook住Application的各个生命周期,这样就可以实现组件的同步加载,需要主动销毁时,则可以将module手动卸载。

Android组件化架构实践,android系统软件开发_第4张图片

4. 依赖和主包管理

随着拆分出来的Module和aar越来越多,每次都需要重复配置依赖和项目基本参数。由于每层(主Module层、业务组件层、功能组件层、基础组件层)之间的依赖都大同小异的,因此抽出一层gradle_component,专门用来配置通用gradle,这样就可以统一依赖,比如:

Android组件化架构实践,android系统软件开发_第5张图片

然后把这些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) { // 切换
sMainAPP’参数来控制主Module和Module的切换。

if(isMainAPP) { // 切换

你可能感兴趣的:(程序员,架构,移动开发,android)