Android Transform基础配置

440 阅读4分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 1天,点击查看活动详情

接上回Flavor配置文章介绍接入Transform,继续来讲Transform需要实现的几个方法实现及定义

继承 com.android.build.api.transform.Transform ,创建⼀个 Transform 的子类

其创建步骤可以细分为五步,如下所示:

  • 重写 getName 方法:返回对应的 Task 名称。
  • 重写 getInputTypes 方法:确定对那些类型的结果进行转换。
  • 重写 getScopes 方法:指定插件的适用范围。
  • 重写 isIncremental 方法:表示是否支持增量更新。
  • 重写 transform 方法:进行具体的转换过程。
getName

每一个 Transform 都有一个与之对应的 Transform task,这里便是返回的 task name。 它会出现在 app/build/intermediates/transforms 目录下。

 /**
  * 每一个 Transform 都有一个与之对应的 Transform task,
  * 这里便是返回的 task name。它会出现在
  * app/build/intermediates/transforms 目录下
  *
  * @return Transform Name
  */
 @Override
 String getName() {
     return "MyCustomTransform"
 }
重写 getInputTypes 方法

getInputTypes 方法用于确定我们需要对哪些类型的结果进行转换:如字节码、资源⽂件等等。 目前 ContentType 有六种枚举类型,通常我们使用比较频繁的有前两种,如下所示:

  • 1、CONTENT_CLASS:表示需要处理 java 的 class 文件。
  • 2、CONTENT_JARS:表示需要处理 java 的 class 与 资源文件。
  • 3、CONTENT_RESOURCES:表示需要处理 java 的资源文件。
  • 4、CONTENT_NATIVE_LIBS:表示需要处理 native 库的代码。
  • 5、CONTENT_DEX:表示需要处理 DEX 文件。
  • 6、CONTENT_DEX_WITH_RESOURCES:表示需要处理 DEX 与 java 的资源文件。

因为我们需要修改的是字节码,所以直接返回 TransformManager.CONTENT_CLASS 即可,代码如下所示:

/**
 * 需要处理的数据类型,目前 ContentType
 * 有六种枚举类型,通常我们使用比较频繁的有前两种:
 *      1、CONTENT_CLASS:表示需要处理 java 的 class 文件。
 *      2、CONTENT_JARS:表示需要处理 java 的 class 与 资源文件。
 *      3、CONTENT_RESOURCES:表示需要处理 java 的资源文件。
 *      4、CONTENT_NATIVE_LIBS:表示需要处理 native 库的代码。
 *      5、CONTENT_DEX:表示需要处理 DEX 文件。
 *      6、CONTENT_DEX_WITH_RESOURCES:表示需要处理 DEX 与 java 的资源文件。 
 *
 * @return
 */
@Override
Set<QualifiedContent.ContentTypegetInputTypes() {
    // 用于确定我们需要对哪些类型的结果进行转换:如字节码、资源⽂件等等。
    // return TransformManager.RESOURCES
    return TransformManager.CONTENT_CLASS
}
重写 getScopes 方法:指定插件的适用范围

目前 Scope 有 五种基本类型,如下所示:

  • 1、PROJECT:只有项目内容。
  • 2、SUB_PROJECTS:只有子项目。
  • 3、EXTERNAL_LIBRARIES:只有外部库,
  • 4、TESTED_CODE:由当前变体(包括依赖项)所测试的代码。
  • 5、PROVIDED_ONLY:只提供本地或远程依赖项。

此外,还有一些复合类型,它们是都是由这五种基本类型组成,以实现灵活确定自定义插件的范围,这里通常是指定整个 project,也可以指定其它范围,其代码如下所示:

SCOPE_FULL_PROJECT 是一个 Scope 集合,包含 Scope.PROJECT,

Scope.SUB_PROJECTS, Scope.EXTERNAL_LIBRARIES 这三项,即当前 Transform

的作用域包括当前项目、子项目以及外部的依赖库

/**
 * 表示 Transform 要操作的内容范围,目前 Scope 有五种基本类型:
 *      1、PROJECT                   只有项目内容
 *      2、SUB_PROJECTS              只有子项目
 *      3、EXTERNAL_LIBRARIES        只有外部库
 *      4、TESTED_CODE               由当前变体(包括依赖项)所测试的代码
 *      5、PROVIDED_ONLY             只提供本地或远程依赖项
 *      SCOPE_FULL_PROJECT 是一个 Scope 集合,包含 Scope.PROJECT,
Scope.SUB_PROJECTS, Scope.EXTERNAL_LIBRARIES 这三项,即当前 Transform
的作用域包括当前项目、子项目以及外部的依赖库
 *
 * @return
 */
@Override
Set<? super QualifiedContent.ScopegetScopes() {
    // 适用范围:通常是指定整个 project,也可以指定其它范围
    return TransformManager.SCOPE_FULL_PROJECT
}
重写 isIncremental 方法:表示是否支持增量更新

isIncremental 方法用于确定是否支持增量更新,如果返回 true,TransformInput 会包含一份修改的文件列表,如果返回 false,则会进行全量编译,并且会删除上一次的输出内容。

@Override
boolean isIncremental() {
    // 是否支持增量更新
    // 如果返回 true,TransformInput 会包含一份修改的文件列表
    // 如果返回 false,会进行全量编译,删除上一次的输出内容
    return false
}
重写 transform 方法:进行具体的转换过程

在 transform 方法中,就是用来给我们进行具体的转换过程的。其实现代码如下所示:

/**
 * 进行具体的转换过程
 *
 * @param transformInvocation
 */
@Override
void transform(TransformInvocation transformInvocation) throws
TransformException, InterruptedException, IOException {
    super.transform(transformInvocation)
    println '--------------- MyTransform visit start --------------- '
    def startTime = System.currentTimeMillis()
    def inputs = transformInvocation.inputs
    def outputProvider = transformInvocation.outputProvider
    // 1、删除之前的输出
    if (outputProvider != null)
        outputProvider.deleteAll()
    // Transform 的 inputs 有两种类型,一种是目录,一种是 jar
包,要分开遍历
    inputs.each { TransformInput input ->
        // 2、遍历 directoryInputs(本地 project 编译成的多个 class
⽂件存放的目录)
        input.directoryInputs.each { DirectoryInput directoryInput ->
            handleDirectory(directoryInput, outputProvider)
        }
        // 3、遍历 jarInputs(各个依赖所编译成的 jar 文件)
        input.jarInputs.each { JarInput jarInput ->
            handleJar(jarInput, outputProvider)
        }
    }
    def cost = (System.currentTimeMillis() - startTime) / 1000
    println '--------------- MyTransform visit end --------------- '
    println "MyTransform cost : $cost s"
}
  • 删除之前的输出。
  • 遍历 directoryInputs(本地 project 编译成的多个 class ⽂件存放的目录)。
  • 遍历 jarInputs(各个依赖所编译成的 jar 文件)。