想象你正在组装一辆汽车(Android应用),Gradle 是自动化装配流水线。Transform API 就是在装配过程中拦截零件(.class 文件),让你有机会修改零件(字节码)再继续组装的特殊工位。
核心概念通俗版
- 时机:在 Java 编译成 .class 文件后,打包成 .dex 文件前
- 作用:拦截并修改编译过程中的中间产物(字节码)
- 类比:就像在汽车组装线上,拦截所有发动机(某个类文件),给每个发动机加装涡轮增压(插入性能监控代码)
如何使用 Transform API(步骤详解)
1. 创建自定义 Transform 类
import com.android.build.api.transform.*
class MyCustomTransform extends Transform {
// Transform 名称(必填)
@Override
String getName() {
return "MyCustomTransform"
}
// 处理数据类型(必填)
@Override
Set<QualifiedContent.ContentType> getInputTypes() {
// 通常处理 CLASS 文件
return TransformManager.CONTENT_CLASS
}
// 作用范围(必填)
@Override
Set<QualifiedContent.Scope> getScopes() {
// 示例:当前项目 + 子模块 + 外部库
return TransformManager.SCOPE_FULL_PROJECT
}
// 是否支持增量编译(推荐实现)
@Override
boolean isIncremental() {
return true
}
// 核心处理逻辑(必填)
@Override
void transform(TransformInvocation invocation) {
// 在这里处理字节码
}
}
2. 注册 Transform(在自定义插件中)
class MyPlugin implements Plugin<Project> {
void apply(Project project) {
def android = project.extensions.getByType(AppExtension)
android.registerTransform(new MyCustomTransform())
}
}
AppExtension 是 Android Gradle 插件(AGP)中最核心的配置入口,它代表了 Android 应用模块(com.android.application)的所有构建配置选项。通俗来说,它就是你在 build.gradle 里 android { ... } 代码块背后的实际处理者
关键语法详解
1. 输入/输出处理
void transform(TransformInvocation invocation) {
// 遍历所有输入
invocation.inputs.each { TransformInput input ->
// 处理目录输入(模块源码)
input.directoryInputs.each { DirectoryInput dirInput ->
File dest = invocation.outputProvider.getContentLocation(
dirInput.name,
dirInput.contentTypes,
dirInput.scopes,
Format.DIRECTORY
)
// 复制并处理文件
processDir(dirInput.file, dest)
}
// 处理 JAR 输入(依赖库)
input.jarInputs.each { JarInput jarInput ->
File dest = invocation.outputProvider.getContentLocation(
jarInput.name,
jarInput.contentTypes,
jarInput.scopes,
Format.JAR
)
processJar(jarInput.file, dest)
}
}
}
2. 字节码操作(使用 ASM 示例)
void processClass(File input, File output) {
FileInputStream fis = new FileInputStream(input)
// 1. 解析.class文件
ClassReader cr = new ClassReader(fis)
// 2. 创建ClassWriter
ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_MAXS)
// 3. 创建自定义Visitor修改字节码
ClassVisitor cv = new MyClassVisitor(Opcodes.ASM9, cw)
// 4. 应用修改
cr.accept(cv, 0)
// 5. 输出修改后的字节码
FileOutputStream fos = new FileOutputStream(output)
fos.write(cw.toByteArray())
fos.close()
}
3. 增量编译处理
void transform(TransformInvocation invocation) {
if (!invocation.isIncremental()) {
// 非增量模式:全量清理输出目录
invocation.outputProvider.deleteAll()
}
invocation.inputs.each { input ->
input.directoryInputs.each { dirInput ->
File dir = dirInput.file
File dest = invocation.outputProvider.getContentLocation(...)
if (invocation.isIncremental()) {
// 增量模式:只处理变更文件
dirInput.changedFiles.each { File file, Status status ->
switch (status) {
case Status.ADDED:
case Status.CHANGED:
processFile(file, getOutputFile(dest, file))
break
case Status.REMOVED:
deleteOutputFile(dest, file)
break
}
}
} else {
// 全量处理
processDir(dir, dest)
}
}
}
}
源码调用流程(Android Gradle 插件内部)
-
任务创建:
TaskManager.createPostCompilationTasks()- 在
transformClassesWith...任务链中插入自定义 Transform
-
执行顺序:
-
核心处理:
TransformManager.addTransform()注册 TransformTransformTask负责实际执行TransformStream处理输入输出流
常见应用场景示例
示例 1:方法耗时统计
// 自定义 ClassVisitor
public class TimeCostVisitor extends ClassVisitor {
@Override
public MethodVisitor visitMethod(...) {
MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions);
return new MethodVisitor(Opcodes.ASM9, mv) {
@Override
public void visitCode() {
// 方法开始插入代码
mv.visitMethodInsn(INVOKESTATIC, "java/lang/System", "nanoTime", "()J", false);
mv.visitVarInsn(LSTORE, 1);
super.visitCode();
}
@Override
public void visitInsn(int opcode) {
if ((opcode >= IRETURN && opcode <= RETURN) || opcode == ATHROW) {
// 方法结束插入代码
mv.visitMethodInsn(INVOKESTATIC, "java/lang/System", "nanoTime", "()J", false);
mv.visitVarInsn(LLOAD, 1);
mv.visitInsn(LSUB);
// 调用日志方法
mv.visitLdcInsn(name);
mv.visitMethodInsn(INVOKESTATIC, "com/example/TimeRecorder", "record", "(JLjava/lang/String;)V", false);
}
super.visitInsn(opcode);
}
};
}
}
示例 2:全局异常捕获
public class ExceptionHandlerVisitor extends ClassVisitor {
@Override
public MethodVisitor visitMethod(...) {
MethodVisitor mv = super.visitMethod(...);
if ("onCreate".equals(name)) {
return new TryCatchWrapper(mv, className, name);
}
return mv;
}
}
class TryCatchWrapper extends MethodVisitor {
// 在方法开头添加 try 块
public void visitCode() {
mv.visitTryCatchBlock(...);
super.visitCode();
}
// 在异常处理块插入上报代码
public void visitMaxs(int maxStack, int maxLocals) {
Label end = new Label();
mv.visitLabel(end);
// 插入异常上报代码
mv.visitMethodInsn(INVOKESTATIC, "com/error/CrashReporter", "report", "(Ljava/lang/Throwable;)V", false);
mv.visitInsn(ATHROW);
super.visitMaxs(maxStack, maxLocals);
}
}
示例 3:路由框架自动注册
// 收集所有带@Route注解的类
public void processClass(File file) {
ClassReader cr = new ClassReader(file.bytes)
cr.accept(new ClassVisitor(Opcodes.ASM9) {
String className;
@Override
public void visit(String version, int access, String name, String signature, String superName, String[] interfaces) {
this.className = name;
super.visit(...)
}
@Override
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
if ("Lcom/example/Route;".equals(desc)) {
// 发现路由注解,记录类名
RouteCollector.add(className)
}
return super.visitAnnotation(desc, visible)
}
}, 0)
}
// 生成注册代码
void generateRegistry() {
ClassWriter cw = new ClassWriter(0)
cw.visit(Opcodes.V1_8, ACC_PUBLIC, "com/example/RouteRegistry", null, "java/lang/Object", null)
MethodVisitor mv = cw.visitMethod(ACC_PUBLIC | ACC_STATIC, "init", "()V", null, null)
for (String className : collectedClasses) {
mv.visitLdcInsn(className)
mv.visitMethodInsn(INVOKESTATIC, "com/example/Router", "register", "(Ljava/lang/String;)V", false)
}
mv.visitInsn(RETURN)
mv.visitMaxs(1, 0)
// 写入生成的文件
}
最佳实践与注意事项
-
性能优化:
- 必须实现增量编译(
isIncremental()返回 true) - 使用高效的字节码操作库(ASM 优于 Javassist)
- 避免修改非目标文件(通过 scopes 精确控制)
- 必须实现增量编译(
-
兼容性处理:
// 避免处理 Android 系统类 if (className.startsWith("android/") || className.startsWith("androidx/")) { return } -
调试技巧:
# 查看 Transform 执行顺序 ./gradlew assembleDebug --dry-run # 输出修改后的字节码 -dontobfuscate -dontoptimize -
常见问题排查:
- 类冲突:检查 transform 顺序
- 增量编译失效:正确处理文件状态
- 堆栈溢出:优化 ASM 访问逻辑
总结对比表
| 特性 | Transform API | 注解处理器 (APT) |
|---|---|---|
| 处理阶段 | 字节码级别 (.class) | 源代码级别 (.java) |
| 修改能力 | 可修改现有代码 | 只能生成新代码 |
| 性能影响 | 较大(需操作字节码) | 较小 |
| 典型应用 | 性能监控、热修复 | 代码生成、DI 框架 |
| 技术门槛 | 高(需字节码知识) | 中 |
| 输入类型 | .class 文件 | .java 文件 |
Transform API 是 Android 构建系统中强大的底层工具,它让你能够在编译过程中直接操作字节码,实现诸如性能监控、代码注入、热修复等高级功能