一、什么是 AOP?
AOP就是面向切面的编程,是一种通过预编译方式和运行期动态代理实现程序功能的统一维护的技术。通过AOP技术,我们可以对业务逻辑的各个部分进行分拆,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
二、Android 中使用AOP场景
1. 可以使用AOP进行程序运行时的权限验证.
2. 使用AOP添加日志输出功能,避免因为日志模块修改API导致修改起来很复杂的问题。
3. 监听一些重要的生命周期的运行,并输出日志。
三、AspectJ 核心概念
AOP虽然如Java的OOP一样也是方法论,但是一些先行者也开发出来了一套语言来支持AOP。目前用的比较火的就是AspectJ了,它几乎是一种和Java完全一样的语言,而且完全兼容Java。
使用AspectJ的方法有两种:
- 完全使用AspectJ的语言,AspectJ几乎和Java完全一样,且能在AspectJ中调用Java的任何类库。AspectJ只是多了一些关键词。
- 使用Java语言开发,然后使用@AspectJ进行注解。
一般情况下,我们使用的是第二种方式来使用AspectJ框架。
在使用AspectJ框架时,我们需要了解一下两个概念:Join Points 和 Pointcuts。
1. Join Points
Join Points 是AspectJ中最关键的概念。JPoints就是程序运行时的一些执行点。那么,一个程序中,哪些执行点是JPoints呢?
AspectJ给出了如下的执行点作为JPoints:
- method call : 函数调用,如调用Log.e().
- method execution : 函数执行,如Log.e()内部的执行.
- construction call 构造函数调用:同函数调用类似.
- construction execution 构造函数执行:同函数执行类似.
- filed get/set : 获取变量/设置变量.
- pre-initilization: Object在构造函数中的一些工作
- initilization:Object在构造函数中的工作
- static initilization:类初始化,如类的static{}
- handler: 异常处理,如try catch{xxx}中,对应catch的执行
可以看出,JPoint就是一个程序中的关键函数(包括构造函数)和代码段(staticblock).
为什么AspectJ首先要定义好JPoint呢?我们可以通过AOP的方式打印Log为例,我们在哪里打印log?自然是去一些关键点去打印。那么哪些又是关键点呢?AspectJ定义的这些类型的JPoint就能满足我们大部分的需求。
2. Pointcuts 介绍
在前面的介绍中,我们可知道,一个程序有很多的JPoints,即使同一个函数,还可以分为call类型的和execution类型的JPoint。但显然并不是所有的JPoint,也不是所有的类型的JPoint都是我们需要关注的,那么如何从如此多的JPoint中选择自己想要的JPoints呢?这就需要PointCuts了。
总结一句话为:Pointcuts 能提供方法让开发者选择自己感兴趣的JoinPoints。
四、Android中AOP框架AspectJ的使用
1. AspectJ的基本Api
@Aspect:声明切面,标记类,使用下面注解之前一定要在类使用@Aspec。
Advice(Hook位置说明,标识要插入代码的位置):
@Before:在PointCut之前执行
@After:在PointCut之后执行
@Around:在PointCut之前、之后分别执行
Execute:
execution:当方法执行的时候Hook
call:当方法被调用的时候Hook
2.使用语法示例
@Around("execution(* android.app.Activity.on**(..))")
public void onFunctionCalled(JoinPoint joinPoint) throw Throwable {
}
3.使用Demo
@Retention(AnnotationRetention.BINARY)
@Target(AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_SETTER, AnnotationTarget.PROPERTY_GETTER)
annotation class SingleClick(
val msg: String = "点击过快"
)
@Aspect
class SingleClickAspect {
@Pointcut("execution(@com.包名.SingleClick * *(..))") //方法切入点
fun methodAnnotated() {
}
@Around(value = "methodAnnotated()")//在连接点进行方法替换
@Throws(Throwable::class)
fun aroundJoinPoint(joinPoint: ProceedingJoinPoint) {
// Do Something
}
}
@SingleClick
private fun addCount() {
tvContent.text = "${++count}"
}
注意: 在Android Studio的4.0.1及以后版本使用AspectJ的时候,可能会出现编译问题,谨慎使用。