urlname
type
Post
password
SyncToConfluence
category
Android
date
Mar 8, 2025
slug
81c0c866-215d-4807-9e6f-ba9647172d0e
icon
Button
catalog
summary
tags
Android
Gradle
学习笔记
cover
Status
BusyTime
Status 1
status
Published

引言

在上一篇文章 “Gradle系列——Transform入门之基础概念:定义、作用与核心价值” 中,我们已经初步了解了 Gradle Transform 的基本概念、核心价值以及与 Task 的区别。为了更深入地掌握 Transform 技术,本文将聚焦于 Transform 的 核心组件概念模型。理解这些核心要素,是编写自定义 Transform,并将其有效地应用于 Android 构建流程的关键。
本文将详细剖析 TransformInvocationTransformInputTransformOutput 以及TransformAction 这四大核心组件,并阐述它们之间的相互关系,构建起 Gradle Transform 的完整概念模型。

Transform 的核心组件

Gradle Transform 的强大功能,是由其精巧的核心组件协同运作实现的。理解这些组件的作用和相互关系,有助于我们更好地掌握 Transform 的工作原理,并能更有效地进行自定义扩展。

TransformInvocation 接口:Transform 执行的上下文

TransformInvocation 接口是 Transform 执行过程中的 核心上下文 (Context) 载体。当 Gradle 调度执行一个 Transform 时,它会创建一个 TransformInvocation 实例,并将其传递给 Transform 的 transform() 方法。TransformInvocation 提供了 Transform 执行过程中所需的一切信息,包括:
  • 构建输入 (Inputs):通过 getInputs() 方法获取,包含了 Transform 的所有输入信息,可以是 TransformInput 对象的集合。
  • 构建输出 (Outputs):通过 getOutputs() 方法获取,用于指定 Transform 的输出路径,生成 TransformOutput 对象。
  • 上下文信息 (Context):通过 getContext() 方法获取,提供了构建过程中的全局上下文信息,例如:
    • isIncremental():指示当前构建是否为增量构建。
    • getTemporaryDir():提供用于存放临时文件的目录。
    • getVariantName():获取当前构建变体 (Variant) 的名称,例如 "debug" 或 "release"。
    • getProjectPath()/getRootProjectPath():获取项目路径和根项目路径。
    • 以及其他与构建环境和配置相关的信息。
💡
TransformInvocation 的关键作用在于为 Transform 的执行提供了一个统一的、结构化的环境,使得 Transform 可以在隔离的环境中安全地进行构建产物的转换操作,并能获取必要的构建信息。

代码示例:

  • 上述代码片段中主要展示了在 TransformActiontransform() 方法中,如何通过 TransformInvocation 对象获取构建上下文信息。
  • transformInvocation.getContext().isIncremental() :获取是否为增量构建的标志。
  • transformInvocation.getContext().getVariantName() :获取当前构建变体的名称。
  • transformInvocation.getContext().getTemporaryDir() :获取临时目录的 File 对象。

TransformInput 接口:构建的输入源

TransformInput 接口代表了 Transform 的 输入源,它定义了 Transform 可以接收的构建产物的形式。
TransformInput 主要有两种具体的实现类型:
  • DirectoryInput (目录输入): 表示一个目录形式的输入源。通常用于处理 未打包 的构建产物,例如:
    • Java/Kotlin 源代码编译后的 .class 文件目录
    • Android res/ 目录下的资源文件
    • Native 库的未压缩目录
    • DirectoryInput 允许 Transform 以目录结构访问输入文件,方便对目录下的文件进行遍历和处理。
  • JarInput (Jar 包输入):表示一个 Jar 包形式的输入源。通常用于处理 已打包 的构建产物,例如:
    • 项目依赖的 Jar 包 (Dependency JARs)
    • Android Archive (AAR) 文件中的 classes.jar
    • 插件或工具提供的 Jar 包
    • JarInput 允许 Transform 将 Jar 包视为一个整体进行处理,或者解压 Jar 包后访问其内部的文件。
      TransformInput 的重要性在于它抽象了构建输入的来源形式,使得 Transform 可以统一地处理不同类型的输入,并提供了增量构建的支持。
TransformInput 接口中关键的方法包括:
  • getFile():获取输入的文件对象
    • 对于 DirectoryInput,通常是目录;
    • 对于 JarInput,则是 Jar 包文件。
  • getDirectory() (仅 DirectoryInput 适用):获取目录输入的根目录。
  • getJarFile() (仅 JarInput 适用):获取 Jar 包输入的 Jar 文件。
  • getChangedFiles() (仅在增量构建时有效):获取本次增量构建中发生变化的文件集合,用于支持增量构建。
  • isIncremental():指示当前输入是否为增量构建输入。

代码示例:

  • 上述代码片段主要展示了如何根据不同的输入类型,进行不同的处理。主要是遍历 TransformInvocation 的输入 (transformInvocation.getInputs()),并区分处理 DirectoryInputJarInput 两种类型的输入。
  • 对于 DirectoryInput,代码获取了目录 File 对象 (directoryInput.getFile()),并使用 Files.walkFileTree 遍历目录下的所有文件,并打印文件路径。
  • 对于 JarInput,代码获取了 Jar 包 File 对象 (jarInput.getFile()),并打印 Jar 包路径 (实际应用中需要解压 Jar 包并处理内部文件)。
  • 这段代码示例展示了如何根据不同的输入类型,进行不同的处理。

TransformOutput 接口:构建的输出目标

TransformOutput 接口代表了 Transform 的 输出目标,它定义了 Transform 生成的构建产物应该输出到哪里。与 TransformInput 类似,TransformOutput 也主要有两种具体的实现类型,与输入类型相对应:
  • DirectoryOutput (目录输出): 用于指定输出为 目录形式 的构建产物,通常与 DirectoryInput 类型的输入相对应。例如,将修改后的 .class 文件输出到新的目录。
  • JarOutput (Jar 包输出): 用于指定输出为 Jar 包形式 的构建产物,通常与 JarInput 类型的输入相对应。例如,将处理后的 Jar 包输出到新的路径。
TransformOutput 的作用是管理 Transform 的输出路径,确保输出的构建产物被正确地放置到 Gradle 构建系统的预期位置,并为后续的构建任务提供输入。
TransformOutput 接口中关键的方法包括:
  • getFile():获取输出的文件对象,对于 DirectoryOutput,通常是目录;对于 JarOutput,则是 Jar 包文件。
  • getDirectory() (仅 DirectoryOutput 适用):获取目录输出的根目录。
  • getJarFile() (仅 JarInput 适用):获取 Jar 包输出的 Jar 文件。
💡
Transform 的输出路径通常由 Gradle 构建系统进行管理,无需手动创建输出目录,只需通过 TransformOutput 提供的方法获取输出路径即可。Gradle 保证了输出路径的唯一性和正确性,避免了文件冲突和构建错误。

代码示例

上述代码片段主要展示了如何遍历 TransformInvocation 的输出 (transformInvocation.getOutputs()),并区分处理 DirectoryOutputJarOutput 两种类型的输出。
  • 对于 DirectoryOutput,代码获取了目录 File 对象 (directoryOutput.getFile()),并打印目录输出路径。
  • 对于 JarOutput,代码获取了 Jar 包 File 对象 (jarOutput.getFile()),并打印 Jar 包输出路径。
注意:实际应用中,需要将转换后的文件写入 outputDiroutputJar 目录中。

TransformAction 接口:核心转换逻辑的定义

TransformAction 接口是 Transform 的 灵魂,它定义了 Transform 的 核心转换逻辑 (Transformation Logic)。 开发者需要实现 TransformAction 接口的 transform() 方法,在该方法中编写具体的转换代码,完成构建产物的转换操作。
💡
TransformActiontransform() 方法是 Transform 执行的核心入口点。
其方法签名通常如下:
transform() 方法接收一个 TransformInvocation 对象作为参数,可以从 TransformInvocation 中获取输入 (getInputs())、输出 (getOutputs()) 和上下文信息 (getContext()),然后编写代码来读取输入文件,进行转换处理,并将结果写入到输出文件。
transform() 方法中,可以自由地结合各种编程技术和工具实现一下特定需求,例如:
  • 字节码操作库 (Bytecode Manipulation Libraries):如 ASM、Javassist、Byte Buddy 等,用于修改 Java/Kotlin 字节码。
  • 资源处理库 (Resource Processing Libraries):如 Android Resource Processing 工具 (AAPT2) 或自定义的资源解析和优化工具。
  • 代码生成工具 (Code Generation Tools):如 JavaPoet、KotlinPoet 等,用于生成新的源代码或配置文件。
  • 压缩和解压缩库 (Compression/Decompression Libraries):用于处理 Jar 包、Zip 文件等压缩格式。
💡
TransformActiontransform() 方法是自定义 Transform 的核心代码所在,其逻辑的正确性和效率直接决定了 Transform 的功能和性能。开发过程中,需要根据具体的构建需求,精心设计和实现 transform() 方法中的转换逻辑。

代码示例

  • 上述代码片段展示了 TransformActiontransform() 方法的基本结构。
  • transform() 方法接收 TransformInvocation 对象作为参数。
  • transform() 方法内部,主要是根据需要,编写具体的转换逻辑,包括:
    • transformInvocation.getInputs() 获取输入,并进行处理。
    • 将转换后的产物通过 transformInvocation.getOutputs() 输出。
  • 示例代码中,仅仅打印了开始和结束日志,以及输入输出的遍历框架,实际应用中,需要将 真正的转换逻辑 填充到 // ... 处理输入 ...// ... 输出转换后的产物 ... 的注释位置。

Gradle Transform 的概念模型

Gradle Transform 的概念模型可以用以下几个关键要素来描述:
  1. 输入 (Input): Transform 接收 TransformInput 对象作为输入,TransformInput 可以是 DirectoryInput (目录) 或 JarInput (Jar 包) 两种形式,代表了需要被转换的构建产物。
  1. 转换逻辑 (Transformation Logic): 通过实现 TransformAction 接口的 transform() 方法来定义具体的转换逻辑。转换逻辑负责读取输入,进行处理,并将结果写入输出。
  1. 输出 (Output): Transform 将转换后的构建产物通过 TransformOutput 对象进行输出,TransformOutput 同样可以是 DirectoryOutput (目录) 或 JarOutput (Jar 包) 两种形式,与输入类型相对应。
  1. TransformInvocation 上下文 (Context): 在 Transform 执行过程中,TransformInvocation 对象提供了必要的上下文信息,包括输入、输出、构建环境信息等,供 TransformActiontransform() 方法使用。
  1. Gradle 构建流程集成 (Gradle Build Flow Integration): 自定义的 Transform 需要通过 Android Gradle Plugin 提供的 API 注册到构建流程中,Gradle 会在合适的时机调度执行注册的 Transform,并将其纳入到整体的构建流程中。
notion image

总结

本文深入剖析了 Gradle Transform 的四大核心组件:TransformInvocationTransformInputTransformOutputTransformAction,并构建了 Gradle Transform 的概念模型。理解这些核心组件及其相互关系,是深入学习和应用 Gradle Transform 的关键。
  • TransformInvocation:提供 Transform 执行的上下文环境。
  • TransformInput:定义 Transform 的输入源,可以是目录或 Jar 包。
  • TransformOutput:定义 Transform 的输出目标,与输入类型对应。
  • TransformAction:定义 Transform 的核心转换逻辑,是自定义 Transform 的核心代码所在。

参考

Gradle系列——现代 Android 字节码插桩利器:AsmClassVisitorFactoryGradle系列——Transform入门之基础概念:定义、作用与核心价值
Loading...