本文同步发布于公众号:移动开发那些事:Android编译时动态插入代码原理与实践
Android开发中,编译时动态插入代码是一种高效,并且对业务逻辑是低侵入性的方案,常用于增加通用的埋点能力,或者插入关键日志,本文以编译时动态插入日志为例来说明如何在Android实现编译时动态插入代码。
1 常见的编译时插入代码方案
- APT
- Transform + ASM
- AspectJ(AOP)
1.1 APT(Annotation Processing Tool)
通过自定义注解标记目标方法/类,然后利用APT在编译期解析注解并生成包含代码逻辑的代码,其核心原理为:
- 注解标记与解析:
- 开发者通过自定义注解(如 @DebugLog)标记需要插入日志的方法或类;
- 编译时,APT 的注解处理器(如继承 AbstractProcessor 的类)会扫描所有被标记的代码元素(如方法、字段);
- 代码生成与织入
- 生成辅助类:注解处理器使用代码生成工具(如 JavaPoet)创建新的 Java 类,这些类包含日志逻辑;
- 逻辑注入:生成的代码会通过静态方法调用或代理模式,在目标方法的前后插入日志语句
优点:
- 代码解耦;
- 灵活性强:支持复杂的逻辑,如参数获取,耗时统计
更适用于需要生成新类的场景,如ButterKnife,Dagger2,Arouter ,
1.2 Transform + ASM
基于Gradle Transform ,在编译流程的.class -> dex的阶段,通过ASM或javassit直接修改字节码,插入日志指令;其实现的核心原理为
- 编译流程拦截:通过Transform API 拦截编译流程
- 每个Transform是独立的Task,多个Task按注册顺序形成链式的处理
- 通过getScopes 控制处理范围
- 通过getInputTypes 指定数据类型,如只处理类文件;
- ASM字节码操作
- ClassReader:读取 .class 文件并触发访问事件;
- ClassWriter:生成修改后的字节码。
- ClassVisitor/MethodVisitor:在访问类或方法时插入自定义逻辑
优点:
- 兼容性强,支持第三方库和系统类修改;
- 灵活性高,要可针对特定包,类或方法进行过滤;
适用于需要修改现有代码逻辑(如插入埋点),典型应用场景为:
1.3 AspectJ(AOP)
通过切点Pointcut定义目标方法,在编译期加入(Weaving)日志逻辑,其核心原理为:
- 编译时织入:在 Java 源码编译为字节码阶段,解析开发者定义的切面(Aspect)和切点(Pointcut),将通知(Advice)代码直接插入目标方法的前后或内部。这种织入方式无需运行时反射,性能损耗低;
- 切点表达式: 切点表达式决定了哪些方法会被注入代码,通过语法(如 execution(* android.app.Activity.onCreate(..)))定义需要拦截的连接点(Join Point)
- 通知类型(Advice Types)
- @Before: 在目标方法执行前插入日志(如记录方法调用时间)
- @After : 在方法正常返回或抛出异常后插入日志
- @Around : 完全控制方法执行,可自定义前后逻辑
优点与适用场景
- 无侵入性:无需修改业务代码,通过声明式切面实现日志逻辑与业务解耦,适用于埋点、性能监控等场景;
- 灵活性与高覆盖率:支持通过复杂表达式匹配任意方法(包括第三方库)
- 性能高效:编译期静态织入避免运行时反射或动态代理开销
适用于简单的应用场景,如方法级的日志插入,如果有更复杂的场景,需要使用Transform + ASM来实现更细粒度的控制
2 实战
2.1 APT(Annotation Processing Tool)
使用APT的步骤:
- 定义注解,用于标记需要插入日志的方法,如DebugLog
- 自定义注解处理器:继承于AbstractProcessor,并使用JavaPoet生成新类或增加现有类;
- 注入代码,在生成类中播入日志调用,例如在方法前后添加Log.e语句
2.1.1 定义注解
- package com.example.annotation;
- import java.lang.annotation.ElementType;
- import java.lang.annotation.Retention;
- import java.lang.annotation.RetentionPolicy;
- import java.lang.annotation.Target;
- // 模块名:annotation
- @Retention(RetentionPolicy.CLASS) // 保留到编译期
- @Target(ElementType.METHOD) // 标记在方法上
- public @interface DebugLog {
- }
复制代码 2.1.2 自定义注解处理器
有使用到两个依赖库,需要在项目的build.gradle文件中添加这两个依赖- implementation 'com.google.auto.service:auto-service:1.0.1'
- implementation 'com.squareup:javapoet:1.13.0'
- }
复制代码 注解处理器
[code]// 模块名:compiler@AutoService(Processor.class) // 自动注册处理器public class DebugLogProcessor extends AbstractProcessor { private Filer filer; // 文件生成器 private Messager messager; // 日志输出工具 @Override public synchronized void init(ProcessingEnvironment env) { super.init(env); filer = env.getFiler(); messager = env.getMessager(); } @Override public boolean process(Set |