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、及所有的四大组件信息列表