Android面向切面编程(AOP)

AOP简介

什么是AOP

AOP(Aspect Oriented Programming),中译为面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP(面向对象编程)的延续,是函数式编程的一种衍生范型。AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高代码的灵活性和可拓展性。在Android中,AOP主要用于日志记录,权限控制,性能统计,安全控制,事务处理,异常处理,热修复,埋点等功能。
AOP在日志系统中的应用

AOP术语

通知、增强处理(Advice):就是你想要的功能,也就是上面说的日志、耗时计算等。
连接点(JoinPoint):允许你通知(Advice)的地方,那可就真多了,基本每个方法的前、后(两者都有也行),或抛出异常是时都可以是连接点(spring只支持方法连接点)。AspectJ还可以让你在构造器或属性注入时都行,不过一般情况下不会这么做,只要记住,和方法有关的前前后后都是连接点。
切入点(Pointcut):上面说的连接点的基础上,来定义切入点,你的一个类里,有15个方法,那就有十几个连接点了对吧,但是你并不想在所有方法附件都使用通知(使用叫织入,下面再说),你只是想让其中几个,在调用这几个方法之前、之后或者抛出异常时干点什么,那么就用切入点来定义这几个方法,让切点来筛选连接点,选中那几个你想要的方法。
切面(Aspect):切面是通知和切入点的结合。现在发现了吧,没连接点什么事,连接点就是为了让你好理解切点搞出来的,明白这个概念就行了。通知说明了干什么和什么时候干(什么时候通过before,after,around等AOP注解就能知道),而切入点说明了在哪干(指定到底是哪个方法),这就是一个完整的切面定义。
织入(weaving): 把切面应用到目标对象来创建新的代理对象的过程。
目标对象 : 项目原始的Java组件。
AOP代理 : 由AOP框架生成java对象。

AOP与OOP区别

OOP 是面向名词领域,AOP 面向动词领域,OOP(面向对象编程)针对业务处理过程的实体及其属性和行为进行抽象封装,以获得更加清晰高效的逻辑单元划分,而AOP则是针对业务处理过程中的切面进行提取,它所面对的是处理过程中的某个步骤或阶段,以获得逻辑过程中各部分之间低耦合性的隔离效果。举个例子,一个学生,通过OOP思想,我们可以抽象“学号”,“班级”这样的属性,“权限检查”这一动作片断进行划分,则是AOP的目标领域。

AOP的实现方式和原理

AOP仅仅只是个概念,实现它的方式(工具和库)有以下几种:

AspectJ: 一个 JavaTM 语言的面向切面编程的无缝扩展(适用Android)。
Javassist for Android: 用于字节码操作的知名 java 类库 Javassist 的 Android 平台移植版。
DexMaker: Dalvik 虚拟机上,在编译期或者运行时生成代码的 Java API。
ASMDEX: 一个类似 ASM 的字节码操作库,运行在Android平台,操作Dex字节码
AOP 方法作用时期比对
从上图可知,AOP的实现主要有动态织入方式和动态织入方式,无论何种方法,均准寻AOP思想,知识插入时机不同,如果需要更深入了解,建议阅读《一文读懂 AOP | 你想要的最全面 AOP 方法探讨

Android中使用AspectJ

AspectJ 意思就是Java的Aspect,Java的AOP。由于Android默认是没有ajc编译器的,所以在Android中使用@AspectJ来编写(包括SpringAOP也是如此)。它在代码的编译期间扫描目标程序,根据切点匹配,将开发者编写的Aspect程序编织到目标程序的.class文件中,对目标程序作了重构,目的就是建立目标程序与Aspect程序的连接,从而达到AOP的目的。简单概括:

AspectJ是通过对目标工程的.class文件进行代码注入的方式将通知(Advise)插入到目标代码中。

使用江户网的开源库gradle_plugin_android_aspectjx 可以轻松接入AspectJ

配置环境
  1. 在项目根目录的build.gradle里依赖AspectJX
  2. 在module的build.gradle里应用插件和库
  3. 使用include,exclude命令来过滤需要处理的文件及排除某些文件(包括class文件及jar文件)
aspectjx {
//排除所有package路径中包含`android.support`的class文件及库(jar文件)
	exclude 'android.support'
}

此外,可以使用下面语句来关闭AspectJX功能,默认为打开:

aspectjx {
//关闭AspectJX功能
	enabled false
}
常用注解介绍
  • @Aspect:声明切面,标记类
  • @Pointcut:定义切点,标记方法
  • @Before:前置通知,切点之前执行
  • @Around:环绕通知,切点前后执行
  • @After(切点表达式):后置通知,切点之后执行 @AfterReturning(切点表达式):返回通知,切点方法返回结果之后执行
  • @AfterThrowing(切点表达式):异常通知,切点抛出异常时执行

除了@Aspect*,上述其他注解需要在切面类(@Aspect标注的类)中使用。

代码实现

我们简单创建一个 test() 函数作为切点。

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        test();
    }

    public String test() {
        Log.d("AOPDemo","In test function(PointCut)");
        return "I am return";
    }
}

创建切面类TestAspectJ.java,主要只是点都标注在代码中:

//@Aspect 声明切面类
@Aspect
public class TestAspectJ {

    private static final String INSTANCE_METHOD_CALL =
            "execution(* com.saberhao.aopdemo.MainActivity.test())";

    //1.使用@Pointcut定义切点,方便重复利用
    @Pointcut(INSTANCE_METHOD_CALL) public void pointCut() {
    }

    //2.切点调用前调用
    @Before(INSTANCE_METHOD_CALL)
    public void beforeNoUsePointCut(JoinPoint point) {
        Log.d("AOPDemo","@Before");
    }

    @Before("pointCut()") //3.该注解等价于 @Before(INSTANCE_METHOD_CALL)
    public void before(JoinPoint point) {
        Log.d("AOPDemo","@Before PointCut");
    }

    //4.切点调用后调用
    @After("pointCut()")
    public void after(JoinPoint point) {
        Log.d("AOPDemo","@After PointCut");
    }

    //5.切点有返回返回值后调用
    @AfterReturning(pointcut = "pointCut()", returning = "returnValue") //"pointcut" 同 "value"
    public void afterReturning(JoinPoint point, Object returnValue){
        Log.d("AOPDemo","@AfterReturning PointCut,return value is "+ returnValue);
    }

输出如下:

com.saberhao.aopdemo D/AOPDemo: @Before
com.saberhao.aopdemo D/AOPDemo: @Before PointCut
com.saberhao.aopdemo D/AOPDemo: In test function(PointCut)
com.saberhao.aopdemo D/AOPDemo: @After PointCut
com.saberhao.aopdemo D/AOPDemo: @AfterReturning PointCut,return value is I am return

需要注意的是,@Around环绕通知会拦截原方法内容的执行,我们需要手动放行才可以:

@Around("pointCut()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
     Log.d("AOPDemo","@Around Before");
     //手动放行原方法
     Object result = joinPoint.proceed();
     Log.d("AOPDemo","@Around After");
     return result;
}

执行效果如下:

com.saberhao.aopdemo D/AOPDemo: @Around Before
com.saberhao.aopdemo D/AOPDemo: In test function(PointCut)
com.saberhao.aopdemo D/AOPDemo: @Around After

如果要测试 @AfterThrowing效果,需要修改test() 函数,抛出异常:

public String test() {
    Log.d("AOPDemo","In test function(PointCut)");
    //测试@AfterThrowing时,手动抛出异常
    throw new NullPointerException("check pointcut throwing");
    //return "I am return";
}

测试效果:

com.saberhao.aopdemo D/AOPDemo: @Before
com.saberhao.aopdemo D/AOPDemo: @Before PointCut
com.saberhao.aopdemo D/AOPDemo: In test function(PointCut)
com.saberhao.aopdemo D/AOPDemo: @After PointCut
com.saberhao.aopdemo D/AOPDemo: @AfterThrowing,ex: check pointcut throwing

这里只是简单引入,需要深入了解,可以参考:AOP之@AspectJ技术原理详解

Demo已经上传到GitHub, 点击 这里 获取

其他

AOP技术广泛应用于开源代码中,比较有名的有Retrofit, 实现AOP,也可以考虑使用APT,使用JavaPoet方式可以方便引入,可以参考ButterKnife的实现,后续可以独立开一篇说说~

参考
AOP (面向切面编程) – 百度百科
Comparing Spring AOP and AspectJ
Android面向切面编程(AOP)
一文读懂 AOP | 你想要的最全面 AOP 方法探讨
AOP之@AspectJ技术原理详解

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值