AspectJ 在 Android 中的完整使用指南
一、环境配置(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(); // 获取宿主ActivityString[] 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 = trueaddSerialVoid = 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 中的工程化应用。建议从 日志监控、权限管理 等场景入手,逐步深入字节码层优化。