一、环境配置(Gradle 7.0+ 适配)
1. 项目级 build.gradle
// 注意:沪江插件已停更,推荐官方兼容方案
buildscript {
dependencies {
classpath 'org.aspectj:aspectjtools:1.9.9.1' // AspectJ 工具
}
}
2. 模块级 build.gradle
plugins {
id 'com.android.application'
id 'io.freefair.aspectj' // 官方推荐插件(支持 AGP 7.0+)
}
dependencies {
implementation 'org.aspectj:aspectjrt:1.9.9.1' // 运行时库
}
兼容性说明:若项目含 Kotlin,添加 id 'io.freefair.aspectj.post-compile-weaving'
插件
二、核心语法详解
1. 切点表达式(Pointcut)
表达式示例 | 含义 |
---|---|
execution(public * *(..)) | 所有 public 方法 |
execution(* com.util.*.*(..)) | com.util 包下所有方法 |
@annotation(com.LogTrack) | 被 @LogTrack 注解的方法 |
within(com.ui..*) | com.ui 包及其子包所有类的方法 |
call(* android.util.Log.d(..)) | 拦截 Log.d() 的调用(非执行) |
2. 通知类型(Advice)
@Before("pointcut()") // 方法执行前
@After("pointcut()") // 方法执行后(无论成败)
@AfterReturning(pointcut="", returning="result") // 方法返回后
@AfterThrowing(pointcut="", throwing="ex") // 抛出异常后
@Around("pointcut()") // 完全控制方法执行(最常用)
三、实战案例
案例 1:自动日志追踪
@Aspect
public class DebugLogAspect {
// 切点:标记 @DebugLog 的方法
@Pointcut("execution(@com.example.DebugLog * *(..))")
public void debugLogPointcut() {}
@Around("debugLogPointcut()")
public Object logMethod(ProceedingJoinPoint joinPoint) throws Throwable {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
String methodName = signature.getMethod().getName();
long start = System.currentTimeMillis();
Log.d("AspectJ", "▶️ " + methodName + " 开始 | 参数: " + Arrays.toString(joinPoint.getArgs()));
Object result = joinPoint.proceed(); // 执行原方法
long duration = System.currentTimeMillis() - start;
Log.d("AspectJ", "◀️ " + methodName + " 结束 | 耗时: " + duration + "ms | 结果: " + result);
return result;
}
}
案例 2:权限申请自动化
// 定义权限注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface RequirePermission {
String[] value(); // 需要申请的权限
}
// 切面实现
@Aspect
public class PermissionAspect {
@Around("execution(@RequirePermission * *(..)) && @annotation(permission)")
public Object checkPermission(ProceedingJoinPoint pjp, RequirePermission permission) throws Throwable {
Activity activity = (Activity) pjp.getTarget(); // 获取宿主Activity
String[] perms = permission.value();
if (hasPermissions(activity, perms)) {
return pjp.proceed(); // 已有权限
} else {
// 发起权限申请(需配合 ActivityResultLauncher)
PermissionRequester.request(activity, perms, () -> {
try { pjp.proceed(); } catch (Throwable ignored) {}
});
return null; // 阻断原方法执行
}
}
private boolean hasPermissions(Context ctx, String... perms) {
for (String perm : perms) {
if (ContextCompat.checkSelfPermission(ctx, perm) != PERMISSION_GRANTED)
return false;
}
return true;
}
}
四、高频问题解决方案
问题 1:织入(Weaving)失效
排查步骤:
-
检查是否应用了 AGP 7.0+ 的兼容插件
-
确认
aspectjrt
版本一致性 -
使用命令检查织入结果:
./gradlew clean assembleDebug --debug | grep "ajc"
问题 2:Lambda 表达式支持
在 build.gradle
中添加:
aspectj {
excludeJar "com.android.support" // 排除冲突库
weaveInfo = true
addSerialVoid = true // 修复 Lambda 支持
}
五、性能优化建议
-
避免在切面中执行耗时操作
(如:IO 读写、网络请求) -
精确限定切点范围
// 劣质写法:扫描全包
@Pointcut("execution(* com.app..*.*(..))")
// 优化写法:精确到类
@Pointcut("within(com.ui.HomeActivity) || within(com.service.*Impl)")
编译时排除第三方库
aspectj {
exclude "androidx.*", "com.google.*"
}
六、AspectJ vs 其他方案对比
方案 | 集成难度 | 性能 | 功能完整性 | 适用场景 |
---|---|---|---|---|
AspectJ | ★★★☆ | ⚡⚡⚡ | ✅ 100% | 需要完整 AOP 支持 |
ASM | ★★★★☆ | ⚡⚡⚡⚡ | ⚠️ 手动实现 | 高性能字节码操作 |
动态代理 | ★★☆ | ⚡ | ❌ 仅限接口 | 简单运行时拦截 |
注解处理器 | ★★★☆ | ⚡⚡ | ⚠️ 无执行期 | 生成代码替代运行时逻辑 |
💡 结论:AspectJ 仍是 Android 平台功能最完备的 AOP 方案,适合复杂横切逻辑。
七、高级技巧:编译时代码注入
在 @Around
中动态修改参数:
@Around("execution(* com.payment.process(..))")
public Object encryptPayment(ProceedingJoinPoint pjp) throws Throwable {
Object[] args = pjp.getArgs();
if (args.length > 0 && args[0] instanceof PaymentRequest) {
PaymentRequest request = (PaymentRequest) args[0];
request.setCardToken(AES.encrypt(request.getCardNumber())); // 自动加密
args[0] = request; // 替换参数
}
return pjp.proceed(args); // 传入新参数
}
通过本指南,你可快速掌握 AspectJ 在 Android 中的工程化应用。建议从 日志监控、权限管理 等场景入手,逐步深入字节码层优化。