Booster 架构概述
Booster 的架构设计
可扩展性是 Booster 架构的核心需求,通过功能模块化,让功能开发相对更独立,也方便功能的测试和灰度验证,除此之外,还要解决这些问题:
- 如何让模块的开发变得更容易?
- 当功能模块足够多时,如何保证构建的性能?
- 如何解决开源协议冲突的问题?
为了能很好的解决这些问题,最终 Booster 的架构被设计成如下图所示:
如何实现可扩展?
为了实现功能模块的动态发现与加载,Booster 以 SPI (Service Provider Interface) 为基础,通过 Google AutoService 简化了 SPI 的配置,在 Booster 中提供了两大类 SPI:
Task SPI 用于在 Gradle 的 Task Graph 中插入自定义的 task,例如:booster-task-compression-cwebp在新窗口打开, booster-task-compression-pngquant在新窗口打开 等等。
Transformer SPI 用于在 Transform 的过程中插入自定义的 Transformer 对字节码进行操作。为了照顾到大多数的开发者,Booster 中提供了两种 Transformer SPI:
- 基于 ASM 的 ClassTransformer在新窗口打开
- 基于 Javassist 的 ClassTransformer在新窗口打开
开发者可以根据自己的喜好,选择使用 ASM 或者 Javassist。
如何让开发变得更容易?
在开发体验和便利性上,Booster 主要做了这些事情:
- 抽象 Transform 过程,直接将解析好的 class 文件结构暴露给开发者;
- 将 Transform 与 Gradle 解耦,在非 Gradle 工程中同样可以复用 Booster 的 Transformer;
- 通过 VariantProcessor在新窗口打开 解决注入外部库的依赖问题,使得大规模字节码注入成为可能;
- 从 AGP 3.0 开始,依次对 AGP 的每个版本进行兼容性适配,并抽象出通用的 Android Gradle API在新窗口打开,使得开发者不用关心 AGP 各版本间的差异;
- 提供了一系列实用的工具类库,为功能模块开发提升效率;
如何保证构建的性能?
支持增量构建
无论是 Transform 还是其它的 Task,都支持增量构建。
一次文件系统 I/O
从 Booster 的源码中可以看到,整个 Transform 过程只涉及到一次读写文件系统,所有的 Transformer 都是访问内存中的 Class 结构
Transformer Pipeline 与并行 Transformer
整个 Transform 的过程以单个 JAR 或目录为粒度进行流式处理,各个 Transformer Pipeline 并行执行,最大限度利用 CPU 资源。
并行写磁盘
通过并行方式将 class 写回 JAR 中。
性能测试
通过 JMH 对多个技术方案进行 benchmark 测试,选择性能最优的方案,详见:booster-benchmark在新窗口打开。
如何解决开源协议冲突?
在 Booster 中,除了 Task SPI在新窗口打开 和 Transform SPI在新窗口打开 以外,还提供了一种 SPI —— Command SPI在新窗口打开。
Command SPI在新窗口打开 主要是为了解决在 Apache License 的协议许可下使用 GPL License 的开源软件,如:pngquant 采用了 GPL License 开源许可,直接在项目中使用会有一定的法律风险,所以 Booster 的做法是将部分抽象出来,以 SPI 的形式在运行时动态发现和加载 Command SPI 的实现,这样,既能保证开源许可的合法性,又能使用优秀的 GPL 开源软件。