copy from https://juejin.cn/post/6844903951024783374
| 简述 | 相关模块 |
|---|---|
| 各模块基础依赖、kotlin的基础扩展 | buildSrc、booster-kotlinx |
| 对Project、AGP中的BaseVariant、VariantScope、TransformInvocation等类添加方法扩展, | booster-gradle-api、booster-gradle-v3_0 booster-gradle-v3_2、booster-gradle-v3_3 |
| Booster的作用入口:自定义插件 + 注册自定义Transfrom。封装ASM层解析class文件成ClassNode并分发到下层具体功能实现。自定义ClassTransform、 VariantProcessor接口等提供扩展能力 |
booster-gradle-plugin、booster-transform-asm booster-transform-javassist、booster-transform-spi booster-transform-util、booster-task-spi |
| 提供编译期对Android API插桩的能力 | booster-android-api、booster-android-instrument |
| 具体Booster功能模块 | 其他模块各自实现:性能检测、性能优化、系统问题、应用瘦身等 |
1.buildSrc
apply from: ‘../gradle/booster.gradle'
booster-gradle-xxx、booster-transform-xxx 等,非 booster-android-instrument 相关的模块都使用了该统一配置,应用 kotlin 的相关依赖配置。
并且引入定义在 buildSrc 目录的自定义 Gradle 插件’BuildPropsPlugin’。
BuildPropsPlugin 用处是一个动态生成一个build配置信息的文件
2.booster-gradle-plugin
2.1 BoosterPlugin.kt
负责两件事情:
- 根据 android app 和 library 模块,注册
BoosterTransform - 模仿JAVA中
ServiceLoader的功能,运行时(Gradle 本身是在 JVM 运行)动态查找VariantProcessor的实现对象列表,遍历并调用processor()方法,这样开发者直接在classpath中注册了插件(仅限Task,plugin需要apply plugin才生效)即可使用
2.2 BoosterTransform.kt
对于抽象类 com.android.build.api.transform.Transform,先来一段说明:
- Transform 表示在构建构件的过程中执行的过程
- 多个 Transform 会连接起来按顺序执行,前者产物
Content为后者的入参Input - 同时每个 Transform 可以设置要接收的 content 类型
ContentType和范围Scope,以及产出类型’ContentType’,让整个链更高效 input以TransformInput类型集合接收内容,有两个属性jarInputs和directoryInputs,两者都有ContentType和Scope的信息- 利用
TransformOutputProvider对象,可以创建本 Transform 独立的文件存储空间
BoosterTransform 的执行过程是支持 Gradle 的增量编译,在transform()方法的实现中,把回调的TransformInvocation对象代理给BoosterTransformInvocation,这样通过booster编写的插件都是在 BoosterTransform 中执行,执行顺序由 BoosterTransform 控制
流程逻辑:
增量:-> onPreTransform()-> doIncrementalTransform()-> onPostTransform()非增量:-> 清理构建目录、输出目录-> onPreTransfrom()-> doFullTransform()-> onPostTransform()
2.3 BoosterTransformInvocation.kt
代理默认的TransformInvocation对象并进行扩展,另外还实现了booster-transform-spi模块的相关接口:
TransformContext: 封装了 Booster 在Transform过程中的上下文数据: 类路径、文件目录、应用信息、调试信息等TransformListener: 定义 transform 的前后监听回调方法onPreTransform()和onPostTransform()ArtifactManager: 对 Artifact 的管理,具体为构建过程的各种文件,get()方法使用VariantScope的各种方法一一对应实现。
关键成员属性:
// 使用SPI机制,读取Transformer接口的实现列表
internal val transformers = loadTransformers(project.buildscript.classLoader).sortedBy {
it.javaClass.getAnnotation(Priority::class.java)?.value ?: 0
}
SPI机制( Service Provider Interface),该方案是为某个接口动态寻找服务的机制,类似IOC的思想,也就是通过接口找到实现类的功能。
对于关键doFullTransform()和 doIncrementalTransform()方法,主要了解两点:
- 如何实现增量构建的兼容
- 具体逻辑流程
构建过程中Transform的回调,通过inputs:List<TransformInput>属性获取资源。
public interface TransformInput {
/** Returns a collection of {@link JarInput}. */
@NonNull
Collection<JarInput> getJarInputs();
/** Returns a collection of {@link DirectoryInput}. */
@NonNull
Collection<DirectoryInput> getDirectoryInputs();
}
JarInput和DirectoryInput都是QualifiedContent的子接口,提供方法获取资源的状态:
/**
* The file changed status for incremental execution.
*/
public enum Status {
/** 相对上次构建,没有变化.*/
NOTCHANGED,
/** 相对上次构建,属于新增文件.*/
ADDED,
/** 相对上次构建,有被修改. */
CHANGED,
/** 相对上次构建,被删除. */
REMOVED;
}
显而易见的「增量构建」是发生在两次构建之间的过程,这四种状态涵盖了两次构建中每个文件、文件夹的变化状况。
when (status) {
REMOVED -> 本次编译不需要该文件/文件夹,删除
ADDED, CHANGED -> 该文件/文件夹,需要被编译处理
NOT_CHANGED -> 不需要处理,因为已经有上次构建的.class文件
}
无论是JarInput还是DirectoryInput,都是这样兼容增量构建。
input经过转换后,输出 byteArray给下层的 Transformer,Transformer的实现有两种, AsmTransformer 和 JavassistTransformer ,针对两种不同的AOP方式
3.booster-transform-asm
除了AsmTransform和ClassTransform,其他 kt 文件都是对 ASM API 的类进行方法扩展。
3.1 AsmTransform.kt
在这一层的职责,是把ByteArray -> ClassNode,暴露给下一层进行方法修改操作,然后转换回ByteArray返回上层。
使用 auto-service 库的@AutoService注解,声明AsmTransform按照 SPI 的规范生成相关的 META-INF 文件。
在 build.gradle 中声明依赖:
kapt "com.google.auto.service:auto-service:1.0"
compile 'com.google.auto.service:auto-service:1.0'
保证了前面的BoosterTransformInvocation能通过ServiceLoader加载数据。
override fun transform(context: TransformContext, bytecode: ByteArray): ByteArray {
val diffEnabled = context.getProperty("booster.transform.diff", false)
return ClassWriter(ClassWriter.COMPUTE_MAXS).also { writer ->
this.transformers.fold(ClassNode().also { klass ->
ClassReader(bytecode).accept(klass, 0)
}) { a, transformer ->
this.threadMxBean.sumCpuTime(transformer) {
if (diffEnabled) {
val left = a.textify()
transformer.transform(context, a).also trans@{ b ->
val right = b.textify()
val diff = if (left == right) "" else left diff right
if (diff.isEmpty() || diff.isBlank()) {
return@trans
}
transformer.getReport(context, "${a.className}.diff").touch().writeText(diff)
}
} else {
transformer.transform(context, a)
}
}
}.accept(writer)
}.toByteArray()
}
通过ClassReader把每个byteCode转换为ClassNode,并调用transform方法
3.2 ClassTransform.kt
ClassTransform 是其他 booster-transform-xxx 模块,需要进行字节码插桩时候,需要具体实现的接口。
/**
* Transform the specified class node
*
* @param context The transform context
* @param klass The class node to be transformed
* @return The transformed class node
*/
fun transform(context: TransformContext, klass: ClassNode) = klass
实现者同时要实现TransformListener,可以在 transform 执行前后,游刃有余地处理资源准备和释放。
当然啦,实现者也要使用@AutoService注解,不然AsmTransform是找不到。
4.booster-task-spi
除了transform,booster还提供了创建task的方法
interface VariantProcessor {
fun process(variant: BaseVariant)
}
其实现主要使用场景:
- 通过
BaseVariant,添加自定义 task,并作为编译 task 的依赖,保证自动执行 - 为 project 添加 booster-android-instrument-xxx 相关的模块依赖(即开发者不用手动在自己模块中进行implementation操作)
5. booster-android-api
定义一些android默认的类路径,主要用来在开发阶段使用的时候能够直接引用,而不需要进行依赖android的库,为了 booster-instrument-xxx 模块中,编写 HOOK 代码的时候,能有一定的 Android API 代码编译环境
6.booster-android-instrument
CaughtCallback.java // 代理Handler, 捕获handleMessage()的RuntimeException
CaughtRunnable.java // 代理Runnable, 捕获run()的RuntimeException
Constants.java // android.util.Log的TAG,全局统一使用
Reflection.java // 抽象类,提供常用的反射静态方法复制代码
因为 booster-android-instrument-xxx 模块,是提供具体 HOOK 方法的实现,HOOK 需用利用反射对某些对象进行代理置换,所以这个模块负责提供工具方法。
7.booster-android-gradle-api
提供了对 Android Gradle Plugin 内部 API 的统一封装,通过版本兼容性适配,以消除 Android Gradle Plugin 版本间的差异。
7.1 ResolvedArtifactResults.kt
表示指定的变体的依赖,即的variant的dependencies
ResolvedArtifactResults.kt 中,关键属性的 results 初始化过程:
private val results = listOf(AAR, JAR)
.asSequence()
.map { variant.getArtifactCollection(RUNTIME_CLASSPATH, ALL, it) }
.map { it.artifacts }
.flatten()
.distinctBy { it.id.componentIdentifier.displayName }
.sortedBy { it.id.componentIdentifier.displayName }
.toList()
获取过程的逻辑为:提取 Android 模块项目中,依赖构件文件类型为 AAR 和 JAR,且构件标识为ProjectComponentIdentifier类型的构件,并且按名称去重、排序,得到一个列表results,并围绕这个列表,提供最大名称长度、最大构建文件长度等变量,大概长这个样子

暂时只看到用在Check SNAPSHOT 的功能上
8 booster-transform-util
8.1 transform.kt
主要提供File、ZipFile、Stream 转换为 ByteArray
8.2 ComponentHandler
class ComponentHandler : DefaultHandler() {
val applications = mutableSetOf<String>()
val activities = mutableSetOf<String>()
val services = mutableSetOf<String>()
val providers = mutableSetOf<String>()
val receivers = mutableSetOf<String>()
override fun startElement(uri: String, localName: String, qName: String, attributes: Attributes) {
val name: String = attributes.getValue(ATTR_NAME) ?: return
when (qName) {
"application" -> applications.add(name)
"activity" -> activities.add(name)
"service" -> services.add(name)
"provider" -> providers.add(name)
"receiver" -> receivers.add(name)
}
}
}复制代码
专门解析 AndroidManifest.xml,提取并缓存 Application、及所有的四大组件信息列表
