前言
在打包流程中,我们知道生成.class文件后,利用dx工具生成.dex文件,而利用Transform API可以在生成.class文件后修改.class文件,从而修改源码。我们将Transform注册到AppExtension中,在java compile Task执行后会执行Tramsform类型的task。
具体过程参考以下两篇文章
Transform API
根据transform api官网中的介绍,可以知道gradle从1.5.0-beta1开始,支持第三方插件修改class文件。transform api的目的是为了简化自定义类的注入,使得我们不需要写task。
注册
@Override
void apply(Project project) {
……
def android = project.extensions.getByType(AppExtension)
//AppExtension appExtension = (AppExtension)project.getProperties().get("android");
android.registerTransform(theTransform)
}
transform 抽象类
自定义一个Transform时,需要继承Transform,下面看下几个抽象方法和transform方法。
public abstract class Transform {
//返回自己Transform类的名字
public abstract String getName();
//传入transform类型,class文件或者resource(非res下文件, 而是assests内的资源),也可以多个。
//类型必须是接口QualifiedContent中的DefaultContentType枚举类里的值
//TransformManager中定义了一些,如TransformManager.CONTENT_CLASS
public abstract Set<ContentType> getInputTypes();
//传入transform文件的所属范围,可以多个。TransformManager中定义了一些,如TransformManager.SCOPE_FULL_PROJECT
//类型必须是接口QualifiedContent中的枚举类Scope里的值
public abstract Set<? super Scope> getScopes();
//是否是增量编译
public abstract boolean isIncremental();
//主要执行方法
public void transform(TransformInvocation transformInvocation);
……
}
transform中的TransformInvocation是主要处理的对下,根据api文档,
//中是传过来的输入流,其中有两种格式,一种是jar包格式一种是目录格式。
transformInvocation.getInputs()
//获取到输出目录,最后将修改的文件复制到输出目录,这一步必须做不然编译会报错
transformInvocation.getOutputProvider()
常用的写法如下
@Override
void transform(TransformInvocation transformInvocation) throws TransformException, InterruptedException,
IOException {
super.transform(transformInvocation)
Collection inputs = transformInvocation.getInputs()
TransformOutputProvider outputProvider = transformInvocation.getOutputProvider()
if (!isIncremental() && outputProvider != null) {
//非增量编译,需要删除之前的输出
outputProvider.deleteAll()
}
inputs.each { TransformInput input ->
//遍历directoryInputs
input.directoryInputs.each { DirectoryInput directoryInput ->
// todo 注入代码……
// 获取output目录
def dest = outputProvider.getContentLocation(directoryInput.name,
directoryInput.contentTypes, directoryInput.scopes,
Format.DIRECTORY)
// 将input的目录复制到output指定目录
FileUtils.copyDirectory(directoryInput.file, dest)
}
//遍历jarInputs
input.jarInputs.each { JarInput jarInput ->
//todo 注入代码……
//生成输出路径
def dest = outputProvider.getContentLocation(jarName + md5Name,
jarInput.contentTypes, jarInput.scopes, Format.JAR)
//将输入内容复制到输出
FileUtils.copyFile(jarInput.file, dest)
}
}
}
可以看到上面代码中有进行了遍历,但是注入代码需要Javassist或者ASM等来处理。可以看下一篇