概述
Gradle Transform是Android官方提供给开发者在项目构建阶段即由class到dex转换期间修改class文件的一套api。目前比较经典的应用是字节码插桩、代码注入技术。
基础
- TransformInput:所谓Transform就是对输入的class文件转变成目标字节码文件,TransformInput就是这些输入文件的抽象。目前它包括两部分:DirectoryInput集合与JarInput集合。
- DirectoryInput:它代表着以源码方式参与项目编译的所有目录结构及其目录下的源码文件,可以借助于它来修改输出文件的目录结构、已经目标字节码文件。
- JarInput:它代表着以jar包方式参与项目编译的所有本地jar包或远程jar包,可以借助于它来动态添加jar包。
- TransformOutputProvider:它代表的是Transform的输出,例如可以通过它来获取输出路径。
目前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:用于指明是否是增量构建。
实践
新建一个标准的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' }
采用buildSrc的方式进行开发,并将Transform封装到自定义插件中,如下buildSrc项目结构:
在该buildSrc下的build.gradle中声明Transform API依赖:
repositories { jcenter() } dependencies { compile 'com.android.tools.build:gradle:2.3.3' }
继承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 } }
在自定义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') } } } }
在app module中使用该插件:
apply plugin: demo.PathPlugin
命令行gradle assembleDebug执行构建时,会打印log:
结论
从上述实践过程不难发现Gradle Transform更多的是提供一种可以让开发者参与项目构建的机制,而诸如修改字节码等更加具体的细节则需要开发者去实现。