从组件化到字节码操控:Gradle+ASM全流程实战(AGP 8
2025-03-22 本文已影响0人
野火友烧不尽
(AGP 8.x以下版):Gradle+Transform+Asm自动化注入代码
一、原始方案回顾
1. 核心场景
通过Gradle插件+Transform API+ASM,在编译期自动为标记了@ComponentRegister的类生成注册代码到ComponentManager,实现组件化无反射初始化。
2. 原始代码架构
--> A[App工程]
--> B[自定义Gradle插件]
--> C[Transform扫描class文件]
--> D[ASM修改ComponentManager]
--> E[生成注册代码]
二、升级到AGP 8.x的必要性
| 特性 | Transform API(旧) | AsmClassVisitorFactory(新) |
|---|---|---|
| AGP兼容性 | 最高支持到6.7 | 4.2+(推荐8.1.2) |
| 字节码操作 | 基于文件IO | 内存直接操作,无IO损耗 |
| 增量编译 | 需手动实现 | 内置支持,速度提升40% |
三、ASM核心操作详解
1. 修改类继承关系
目标:让TargetClass继承BaseClass
public class ChangeSuperClassVisitor extends ClassVisitor {
public ChangeSuperClassVisitor(ClassVisitor cv) {
super(Opcodes.ASM9, cv);
}
@Override
public void visit(int version, int access, String name,
String signature, String superName,
String[] interfaces) {
// 修改父类为"com/example/BaseClass"
super.visit(version, access, name, signature, "com/example/BaseClass", interfaces);
}
}
2. 方法织入代码
目标:在onCreate前后插入日志
public class MethodWeavingVisitor extends ClassVisitor {
public MethodWeavingVisitor(ClassVisitor cv) {
super(Opcodes.ASM9, cv);
}
@Override
public MethodVisitor visitMethod(int access, String name,
String desc, String signature,
String[] exceptions) {
MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions);
if ("onCreate".equals(name)) {
return new AdviceAdapter(mv, access, name, desc) {
@Override
protected void onMethodEnter() { // 方法开始
mv.visitLdcInsn("Enter: " + name);
mv.visitMethodInsn(INVOKESTATIC, "Logger", "log", "(Ljava/lang/String;)V", false);
}
@Override
protected void onMethodExit(int opcode) { // 方法结束
mv.visitLdcInsn("Exit: " + name);
mv.visitMethodInsn(INVOKESTATIC, "Logger", "log", "(Ljava/lang/String;)V", false);
}
};
}
return mv;
}
}
3. 处理泛型与注解
目标:打印类上的@Deprecated注解和泛型信息
public class GenericVisitor extends ClassVisitor {
private String className;
public GenericVisitor(ClassVisitor cv) {
super(Opcodes.ASM9, cv);
}
@Override
public void visit(int version, int access, String name,
String signature, String superName,
String[] interfaces) {
className = name;
super.visit(version, access, name, signature, superName, interfaces);
}
@Override
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
if (desc.equals(Type.getDescriptor(Deprecated.class))) {
System.out.println(className + " is deprecated");
}
return super.visitAnnotation(desc, visible);
}
@Override
public void visitTypeParameter(TypeParameter[] typeParameters) {
for (TypeParameter tp : typeParameters) {
System.out.println("Generic: " + tp.getName());
}
}
}
四、AGP 8.x完整实现:组件自动注册
1. 项目结构
project-root/
├─ app/ # 应用模块
│ ├─ src/main/java/
│ │ ├─ com/example/
│ │ │ ├─ ComponentManager.java # 自动生成注册代码
│ │ │ └─ components/
│ │ │ ├─ ComponentA.java # 标记@ComponentRegister
│ └─ build.gradle
└─ buildSrc/ # Gradle插件模块
└─ src/main/java/
└─ com/example/
├─ ComponentPlugin.java # 插件入口
└─ visitor/
├─ ComponentClassVisitorFactory.java # Visitor工厂
└─ ComponentClassVisitor.java # 核心Visitor
2. 核心代码实现
Step 1:定义注解与接口
// IComponent.java
public interface IComponent { void init(); }
// ComponentRegister.java
@Retention(RetentionPolicy.CLASS)
public @interface ComponentRegister {
String priority() default "NORMAL";
}
Step 2:Gradle插件入口
// ComponentPlugin.kt
class ComponentPlugin : Plugin<Project> {
override fun apply(project: Project) {
val android = project.extensions.getByType(AppExtension::class.java)
android.registerTransform(
AsmClassVisitorFactory.create(
ComponentClassVisitorFactory::class.java,
InstrumentationScope.PROJECT,
true // 开启增量编译
)
)
}
}
Step 3:Visitor工厂与核心逻辑
// ComponentClassVisitorFactory.java
public class ComponentClassVisitorFactory
implements AsmClassVisitorFactory<ComponentClassVisitorFactory.Parameters> {
public static class Parameters { }
@Override
public ClassVisitor createClassVisitor(
ClassContext classContext,
ClassVisitor nextClassVisitor
) {
return new ComponentClassVisitor(nextClassVisitor);
}
@Override
public boolean isInstrumentable(ClassData classData) {
return classData.getAnnotations().contains(
"Lcom/example/ComponentRegister;"
);
}
}
// ComponentClassVisitor.java
public class ComponentClassVisitor extends ClassVisitor {
private final List<String> componentClasses = new ArrayList<>();
private String currentClassName;
public ComponentClassVisitor(ClassVisitor cv) {
super(Opcodes.ASM9, cv);
}
@Override
public void visit(int version, int access, String name,
String signature, String superName,
String[] interfaces) {
currentClassName = name;
super.visit(version, access, name, signature, superName, interfaces);
}
@Override
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
if (desc.equals(Type.getDescriptor(ComponentRegister.class))) {
componentClasses.add(currentClassName);
}
return super.visitAnnotation(desc, visible);
}
@Override
public void visitEnd() {
if ("com/example/ComponentManager".equals(currentClassName)) {
generateRegistrationCode();
}
super.visitEnd();
}
private void generateRegistrationCode() {
MethodVisitor mv = cv.visitMethod(
Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC,
"registerComponents",
"()V",
null,
null
);
mv.visitCode();
// 按优先级排序组件(示例)
componentClasses.stream()
.sorted(Comparator.comparingInt(c -> {
// 从注解中获取优先级
return ComponentRegisterPriority.getPriority(c);
}))
.forEach(this::generateComponentCode);
mv.visitInsn(Opcodes.RETURN);
mv.visitMaxs(0, 0);
mv.visitEnd();
}
private void generateComponentCode(String className) {
mv.visitTypeInsn(Opcodes.NEW, className);
mv.visitInsn(Opcodes.DUP);
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, className, "<init>", "()V", false);
mv.visitMethodInsn(Opcodes.INVOKESTATIC,
"com/example/ComponentManager",
"addComponent",
"(Lcom/example/IComponent;)V",
false
);
}
}
五、ASM高级技巧与工具
1. 使用AdviceAdapter简化开发
优势:自动处理操作数栈,避免手动计算偏移量
public class AdviceAdapterDemo extends AdviceAdapter {
public AdviceAdapterDemo(MethodVisitor mv, int access,
String name, String desc) {
super(Opcodes.ASM9, mv, access, name, desc);
}
@Override
protected void onMethodEnter() {
// 插入局部变量
int localVar = newLocal(Type.getType(String.class));
visitLdcInsn("Method entered: " + name);
visitVarInsn(ASTORE, localVar);
}
}
2. ASM Bytecode Viewer使用
- 下载:GitHub Release
- 反编译class:
javap -c -p ModifiedClass.class
-
对比字节码:通过
Compare功能查看修改前后差异
六、常见问题与解决方案
1. 类找不到异常
现象:ClassNotFoundException: ComponentManager
原因:插件未正确生成类或混淆导致
解决方案:
- 检查
InstrumentationScope是否包含目标类 - 添加ProGuard规则:
-keep class com.example.ComponentManager { *; }
2. 栈帧计算错误
现象:java.lang.VerifyError
原因:手动计算栈帧大小错误
解决方案:使用ClassWriter.COMPUTE_FRAMES模式
ClassWriter writer = new ClassWriter(ClassReader, ClassWriter.COMPUTE_FRAMES);
3. 泛型信息丢失
原因:ASM默认不保留泛型签名
解决方案:在ClassReader.accept()中启用EXPAND_FRAMES
reader.accept(cv, ClassReader.EXPAND_FRAMES);
七、性能优化
1. 多线程处理
// 使用ForkJoinPool并行扫描类
ForkJoinPool.commonPool().submit(() -> {
inputs.forEach(this::processInput);
}).get();
2. 缓存未修改文件
// 插件中实现增量缓存
transformInvocation.outputProvider.deleteAll()
inputs.forEach { input ->
input.incrementalFileSet?.let {
it.getAddedFiles().forEach(::processFile)
it.getChangedFiles().forEach(::processFile)
}
}
八、总结
本文通过AGP 8.x的AsmClassVisitorFactory和ASM 9,实现了从组件化注册到字节码精细操控的全流程。核心优势包括:
- 零反射:编译期完成所有注册逻辑
- 高性能:内置增量编译,速度提升40%
- 强扩展性:通过注解和接口轻松扩展功能