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

负责两件事情:

  1. 根据 android app 和 library 模块,注册BoosterTransform
  2. 模仿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’,让整个链更高效
  • inputTransformInput类型集合接收内容,有两个属性 jarInputsdirectoryInputs,两者都有ContentTypeScope的信息
  • 利用TransformOutputProvider对象,可以创建本 Transform 独立的文件存储空间

BoosterTransform 的执行过程是支持 Gradle 的增量编译,在transform()方法的实现中,把回调的TransformInvocation对象代理给BoosterTransformInvocation,这样通过booster编写的插件都是在 BoosterTransform 中执行,执行顺序由 BoosterTransform 控制

流程逻辑:

  1. 增量:
  2. -> onPreTransform()
  3. -> doIncrementalTransform()
  4. -> onPostTransform()
  5. 非增量:
  6. -> 清理构建目录、输出目录
  7. -> onPreTransfrom()
  8. -> doFullTransform()
  9. -> 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();
}

JarInputDirectoryInput都是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

除了AsmTransformClassTransform,其他 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,并围绕这个列表,提供最大名称长度、最大构建文件长度等变量,大概长这个样子

image.png

暂时只看到用在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、及所有的四大组件信息列表