幽淆 发表于 2025-6-2 21:27:44

Android编译时动态插入代码原理与实践

本文同步发布于公众号:移动开发那些事: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'
}注解处理器
// 模块名: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
页: [1]
查看完整版本: Android编译时动态插入代码原理与实践