Android的AOP编程

参考资料 https://blog.csdn.net/nthack5730/article/details/45604031

理论

可以通过预编译方式和运行期动态代理实现在不修改源代码的情况下给程序动态统一添加功能的一种技术。AOP实际是GoF设计模式的延续,设计模式孜孜不倦追求的是调用者和被调用者之间的解耦,提高代码的灵活性和可扩展性,AOP可以说也是这种目标的一种实现。
——百度百科

Aspectj

AspectJ是一个面向切面的框架,它扩展了Java语言。AspectJ定义了AOP语法,所以它有一个专门的编译器用来生成遵守Java字节编码规范的Class文件。
——百度百科

Aspectj在Android上的使用

配置

使用开源项目https://github.com/HujiangTechnology/gradle_plugin_android_aspectjx
在根目录下的build.gradle加上

1
2
3
4
5
6
dependencies {
......
.......
classpath 'com.hujiang.aspectjx:gradle-android-plugin-aspectjx:2.0.0'
}

在app上的build.gradle最顶部加上

1
apply plugin: 'com.hujiang.android-aspectjx'

使用

先看一些代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
@Aspect
public class AspectTest {
//一般操作
@After("execution(* cczhr.com.aoptest.MainActivity.on*(android.os.Bundle))")
public void onActivityMethodAfter(JoinPoint joinPoint) throws Throwable {}
//捕获构造方法
@After("execution(cczhr.com.aoptest.Demo.new(..))")
public void a(JoinPoint joinPoint) {
Log.e(TAG, "连接点所在位置的相关信息"+ joinPoint.toString() );
Log.e(TAG, "连接点所在位置的简短信息" +joinPoint.toShortString());
Log.e(TAG, "连接点所在位置的全部信息" +joinPoint.toLongString());
Log.e(TAG, "连接点类型" +joinPoint.getKind());
Log.e(TAG, "AOP代理对象" +joinPoint.getThis().getClass().getName()+"哈希码"+joinPoint.getThis().hashCode());
Log.e(TAG, "目标对象" +joinPoint.getTarget().getClass().getName()+"哈希码"+joinPoint.getTarget().hashCode());
Log.e(TAG, "方法传参数组" +joinPoint.getArgs().length);
Log.e(TAG, "连接点所在文件中的位置" +joinPoint.getSourceLocation());
Log.e(TAG, "连接点对象信息" +joinPoint.getSignature().toString()+"----"+joinPoint.getSignature().toLongString()+"----"+joinPoint.getSignature().getModifiers());
}
//捕获访问属性
@Around("get(int cczhr.com.aoptest.Demo.age)")
public int b(ProceedingJoinPoint joinPoint) throws Throwable {}
//捕获静态代码块
@Before("staticinitialization(cczhr.com.aoptest.Demo)")
public void c(JoinPoint joinPoint) { }
//方法返回值
@AfterReturning(pointcut = "execution(* cczhr.com.aoptest.MainActivity.returnNumber(..))", returning = "myNumber")
public void getMyNumber(JoinPoint joinPoint,int myNumber) {}
//捕获加了try catch的异常 只能精准匹配 handler 不支持 @After 与 @Around,且异常只支持编译时匹配
@Before("handler(java.lang.Exception)")
public void handler() { }
//捕获抛出的异常
@AfterThrowing(pointcut = "call(* cczhr.com.aoptest.MainActivity.getMessage(..))", throwing = "throwable")
public void getThrows(Throwable throwable) {}
//注解
@Around("execution(@cczhr.com.aoptest.DebugTool * *(..))&& @annotation(debugTool)")
public void onDebugToolMethodBefore(ProceedingJoinPoint joinPoint,DebugTool debugTool) throws Throwable {}
}
//Pointcut写法
@Pointcut("get(int cczhr.com.aoptest.MainActivity.data)")
@Around("fieldget()")
public int fieldget(ProceedingJoinPoint thisJoinPoint) throws Throwable {
Object proceed = thisJoinPoint.proceed();
return (int) proceed; //必须return 否则报错(applying to join point that doesn't return void: field-get)
}

声明

其中@Aspect代表声明AspectJ文件,让编译器自动去解析识别。

具体插入方式(Advice)

参数 说明
@Before 插入点运行之前
@After 插入点运行之前
@Around 插入点运行前后 (不能与@Before@After共用)其中@Around的 proceedingJoinPoint.proceed();代表执行原始的方法可以在前后插入代码
@AfterThrowing 捕获抛出的异常
@AfterReturning 获取方法返回值

切入点(Join Points)

注意:途中的testAOP为被切入的方法
参数 说明
execution 方法内部切入
call 方法外部切入
staticinitialization 静态代码块切入
handler 捕获异常的切入
set/get 写读属性的切入

常用识别标识(Signature)

类型 参数 说明
一般类型 * 为不指定 参数顺序为: 修饰符 返回值.类名.方法(参数)
注解 @完整路径 例如:@cczhr.com.aoptest.DebugTool (..)
构造方法 修饰符 返回值.类名.new(参数) 例如:cczhr.com.aoptest.Demo.new(..)
属性 修饰符 返回值.类名.成员变量名 例如:int cczhr.com.aoptest.Demo.age
异常 类名 例如:java.lang.Exception

其中参数中

1
2
3
4
5
6
7
()//代表没有任何参数
(..)//代表可以任何参数
(int,String) //代表参数只有int String两个
(int,..)//代表第一个参数为int 后面参数不定
(..,String)//代表前面参数不定 最后一个参数为String
(*,String)//代表第一个参数不定第二个参数为String
(int,*)//代表第一个参数in第二个参数不定