Gradle Transform

Gradle Transform是Android构建期间修改class文件的API,常用于字节码插桩和代码注入。它包括TransformInput、DirectoryInput、JarInput和TransformOutputProvider等概念,允许开发者在构建过程中改变类文件。实践部分展示了如何创建自定义Transform并应用于项目,以实现构建时的代码修改。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

概述

Gradle Transform是Android官方提供给开发者在项目构建阶段即由class到dex转换期间修改class文件的一套api。目前比较经典的应用是字节码插桩、代码注入技术。

基础
  1. TransformInput:所谓Transform就是对输入的class文件转变成目标字节码文件,TransformInput就是这些输入文件的抽象。目前它包括两部分:DirectoryInput集合与JarInput集合。
  2. DirectoryInput:它代表着以源码方式参与项目编译的所有目录结构及其目录下的源码文件,可以借助于它来修改输出文件的目录结构、已经目标字节码文件。
  3. JarInput:它代表着以jar包方式参与项目编译的所有本地jar包或远程jar包,可以借助于它来动态添加jar包。
  4. TransformOutputProvider:它代表的是Transform的输出,例如可以通过它来获取输出路径。
  5. 目前Transform包含如下方法:
    这里写图片描述

    • getName:用于指明本Transform的名字,也是代表该Transform的task的名字
    • getInputTypes:用于指明Transform的输入类型,可以作为输入过滤的手段。在TransformManager定义了如下的几种类型:
    public static final Set<ContentType> CONTENT_CLASS = ImmutableSet.of(CLASSES);
    public static final Set<ContentType> CONTENT_JARS = ImmutableSet.of(CLASSES, RESOURCES);
    public static final Set<ContentType> CONTENT_RESOURCES = ImmutableSet.of(RESOURCES);
    public static final Set<ContentType> CONTENT_NATIVE_LIBS = ImmutableSet.of(
            ExtendedContentType.NATIVE_LIBS);
    public static final Set<ContentType> CONTENT_DEX = ImmutableSet.of(
            ExtendedContentType.DEX);
    public static final Set<ContentType> CONTENT_JACK = ImmutableSet.of(JACK);
    public static final Set<ContentType> DATA_BINDING_ARTIFACT = ImmutableSet.of(
            ExtendedContentType.DATA_BINDING, CLASSES);
    • getScopes:用于指明Transform的作用域,在TransformManager定义了如下的三种种类型:
    public static final Set<Scope> SCOPE_FULL_PROJECT = Sets.immutableEnumSet(
            Scope.PROJECT,
            Scope.PROJECT_LOCAL_DEPS,
            Scope.SUB_PROJECTS,
            Scope.SUB_PROJECTS_LOCAL_DEPS,
            Scope.EXTERNAL_LIBRARIES);
    public static final Set<QualifiedContent.ScopeType> SCOPE_FULL_INSTANT_RUN_PROJECT =
            new ImmutableSet.Builder<QualifiedContent.ScopeType>()
                    .addAll(SCOPE_FULL_PROJECT)
                    .add(InternalScope.MAIN_SPLIT)
                    .build();
    public static final Set<Scope> SCOPE_FULL_LIBRARY = Sets.immutableEnumSet(
            Scope.PROJECT,
            Scope.PROJECT_LOCAL_DEPS);
    • isIncremental:用于指明是否是增量构建。
实践
  1. 新建一个标准的Android项目,在app module下的build.gradle添加如下依赖:

    dependencies {   
        compile localGroovy()
        compile 'com.android.tools.build:transform-api:1.5.0'
        compile 'commons-io:commons-io:2.5'
    }
  2. 采用buildSrc的方式进行开发,并将Transform封装到自定义插件中,如下buildSrc项目结构:
    这里写图片描述

  3. 在该buildSrc下的build.gradle中声明Transform API依赖:

    repositories {
        jcenter()
    }
    
    dependencies {
        compile 'com.android.tools.build:gradle:2.3.3'
    }
  4. 继承Transform,并重写transform方法

    package demo
    
    import com.android.build.api.transform.Context
    import com.android.build.api.transform.DirectoryInput
    import com.android.build.api.transform.Format
    import com.android.build.api.transform.JarInput
    import com.android.build.api.transform.Transform
    import com.android.build.api.transform.TransformException
    import com.android.build.api.transform.TransformInput
    import com.android.build.api.transform.TransformOutputProvider
    
    import com.android.build.gradle.internal.pipeline.TransformManager
    import com.android.build.api.transform.QualifiedContent.Scope
    import com.android.build.api.transform.QualifiedContent.ContentType
    import org.apache.commons.codec.digest.DigestUtils
    import org.apache.commons.io.FileUtils
    import org.gradle.api.Project
    
    class PathTransform extends Transform {
    
        Project project
    
        public PathTransform(Project project) {
            this.project = project
        }
    
        @Override
        void transform(
            Context context,
            Collection<TransformInput> inputs,
            Collection<TransformInput> referencedInputs,
            TransformOutputProvider outputProvider, boolean isIncremental) throws IOException, TransformException, InterruptedException {
    
            System.out.println("=======================================doPathTransform{ context=${context}, inputs=${inputs}, referencedInputs=${referencedInputs}, outputProvider=${outputProvider}, isIncremental=${isIncremental}")
    
        }
    
        @Override
        String getName() {
            return PathTransform.simpleName
        }
    
        @Override
        Set<ContentType> getInputTypes() {
            return TransformManager.CONTENT_CLASS
        }
    
        @Override
        Set<? super Scope> getScopes() {
            return TransformManager.SCOPE_FULL_PROJECT
        }
    
        @Override
        boolean isIncremental() {
            return false
        }
    }
  5. 在自定义task或plugin中注册该Transform,注意AppExtension就是app中build.gradle中android块:

    package demo
    
    import org.gradle.api.Plugin
    import org.gradle.api.Project
    import com.android.build.gradle.AppExtension
    
    class PathPlugin implements Plugin<Project> {
    
        @Override
        void apply(Project project) {
    
            def android = project.extensions.getByType(AppExtension)
            android.registerTransform(new PathTransform(project))
    
            project.task('transformPath') {
                doLast {
                    System.out.println('+++++++++++++++++++++transformPath task')
                }
            }
        }
    }
  6. 在app module中使用该插件:

    apply plugin: demo.PathPlugin
  7. 命令行gradle assembleDebug执行构建时,会打印log:
    这里写图片描述

结论

从上述实践过程不难发现Gradle Transform更多的是提供一种可以让开发者参与项目构建的机制,而诸如修改字节码等更加具体的细节则需要开发者去实现。

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值