Android中的AOP

说一件开心的事情,我的这篇文章_AOP编程将被刊登在 鸿洋的微信公众号上,开心ing…

今天值得高兴的事情


回归正题了。

来源:知乎 欲眼熊猫

面向切面编程(AOP是Aspect Oriented Program的首字母缩写) ,我们知道,面向对象的特点是继承、多态和封装。而封装就要求将功能分散到不同的对象中去,这在软件设计中往往称为职责分配。实际上也就是说,让不同的类设计不同的方法。这样代码就分散到一个个的类中去了。这样做的好处是降低了代码的复杂程度,使类可重用。 但是人们也发现,在分散代码的同时,也增加了代码的重复性。什么意思呢?比如说,我们在两个类中,可能都需要在每个方法中做日志。按面向对象的设计方法,我们就必须在两个类的方法中都加入日志的内容。也许他们是完全相同的,但就是因为面向对象的设计让类与类之间无法联系,而不能将这些重复的代码统一起来。 也许有人会说,那好办啊,我们可以将这段代码写在一个独立的类独立的方法里,然后再在这两个类中调用。但是,这样一来,这两个类跟我们上面提到的独立的类就有耦合了,它的改变会影响这两个类。那么,有没有什么办法,能让我们在需要的时候,随意地加入代码呢?这种在运行时,动态地将代码切入到类的指定方法、指定位置上的编程思想就是面向切面的编程。 一般而言,我们管切入到指定类指定方法的代码片段称为切面,而切入到哪些类、哪些方法则叫切入点。有了AOP,我们就可以把几个类共有的代码,抽取到一个切片中,等到需要时再切入对象中去,从而改变其原有的行为。这样看来,AOP其实只是OOP的补充而已。OOP从横向上区分出一个个的类来,而AOP则从纵向上向对象中加入特定的代码。有了AOP,OOP变得立体了。如果加上时间维度,AOP使OOP由原来的二维变为三维了,由平面变成立体了。从技术上来说,AOP基本上是通过代理机制实现的。 AOP在编程历史上可以说是里程碑式的,对OOP编程是一种十分有益的补充。

* 在运行时,动态地将代码切入到类的指定方法、指定位置上的编程思想就是面向切面的编程。*

场景:

分离复杂度,逻辑事务与非逻辑事务分离
具体场景有:
* 日志收集;
* 权限验证;
* 数据验证;
* 监控;
* 更多
逻辑代码与非逻辑代码示例

基本需要了解的概念

Join Points

JPoints就是程序运行时的一些执行点。那么,一个程序中,哪些执行点是JPoints呢?比如:
  l  一个函数的调用可以是一个JPoint。比如Log.e()这个函数。e的执行可以是一个JPoint,而调用e的函数也可以认为是一个JPoint。
  l  设置一个变量,或者读取一个变量,也可以是一个JPoint。比如Demo类中有一个debug的boolean变量。设置它的地方或者读取它的地方都可以看做是JPoints。

我们经常看到的依赖注入经常是这样用的:

    @BindView(R.id.textView_commend_2)
    CommonTabLayout tl_2;

    @OnClick(R.id.textView_commend_2)
    public void onClickAdd() {
          //在该方法运行时  也是执行点
    }

上面代码的作用相信大家都知道,那么在这里 设置变量tl_2 时是执行点,声明方法onClickAdd 也是执行点。
甚至 我做一个操作 ,比如点击一个按钮,这个按钮会执行onClickAdd方法,那么在我调用 onClickAdd方法之前可以是执行点、在执行onCLickAdd的过程中也可以是执行点。
这段话说的有点绕口,如果没有表达清楚,请见谅。

AspectJ中的Join Point

Join Points说明示例
method call函数调用比如调用Log.e(),这是一处JPoint
method execution函数执行比如Log.e()的执行内部,是一处JPoint。注意它和method call的区别。method call是调用某个函数的地方。而execution是某个函数执行的内部。
constructor call构造函数调用和method call类似
constructor execution构造函数执行和method execution类似
field get获取某个变量比如读取DemoActivity.debug成员
field set设置某个变量比如设置DemoActivity.debug成员
pre-initializationObject在构造函数中做得一些工作。
initializationObject在构造函数中做得工作
static initialization类初始化比如类的static{}
handler异常处理比如try catch(xxx)中,对应catch内的执行
advice execution这个是AspectJ的内容我自己也不太懂
Pointcuts

Pointcuts是什么呢? Pointcuts的功能 是从众多的 JoinPoint中找到指定的执行点;

在上面的代码中 我可以通过注解拿到 变量和方法;然而通过上面的解释我们知道一个变量或者方法存在好多个执行点,我方法在即将执行之前是执行点,执行过程中也可以是个执行点,set变量是执行点,get变量也是执行点,那么我拿到了变量或者方法,我在什么时候做事情呢? 我通过什么判断呢? Pointcuts就是做这个事情的。

* 白话:pointCuts 的作用是找到指定的注解,通过注解拿到声明的 变量或者方法,然后设置 变量/方法 在指定的执行点 joinPoint 。*
然后我们就可以在 指定的执行点下做我们自定义的一系列动作。

通过注解拿到声明的变量或者方法

* Pointcuts的目标是提供一种方法使得开发者能够选择自己感兴趣的JoinPoints。*

pointcuts中最常用的选择条件和Joinpoint的类型密切相关
切点对应的执行点

题目: 小明的旧娃娃质量不好,没用几下折腾几下就破掉了,小明很郁闷,然后小明去买新娃娃,这个店家给他推荐了一个苍老师版本的娃娃,小明在付款的时候突然想到旧娃娃的质量不好,提出要验验货。‘

* 答案:*

    @Pointcut("execution(@com.wenld.aspectjdemo.买娃娃  * *(..))")
    public void executionAspectJ() {
          验货...嘿嘿嘿
          if(验货通过) 买
    }

声明我们要找的买娃娃 注解,注解找到的方法 选择苍老师款+付款execution)的时候 ,executionAspectJ验验货….

advice和aspect

恭喜,看到这个地方来,AspectJ的核心部分就掌握一大部分了。现在,我们知道如何通过pointcuts来选择合适的JPoint。那么,下一步工作就很明确了,选择这些JPoint后,我们肯定是需要干一些事情的。比如前面例子中的 before 小明验货之类的。这其实JPoint在执行前,执行后,都执行了一些我们设置的代码。

advice的类型

关键词说明示例
before()before advice表示在JPoint执行之前,需要干的事情
after()after advice表示JPoint自己执行完了后,需要干的事情。
after():returning(返回值类型) after():throwing(异常类型)returning和throwing后面都可以指定具体的类型,如果不指定的话则匹配的时候不限定类型假设JPoint是一个函数调用的话,那么函数调用执行完有两种方式退出,一个是正常的return,另外一个是抛异常。 注意,after()默认包括returning和throwing两种情况
返回值类型 around()before和around是指JPoint执行前或执行后备触发,而around就替代了原JPointaround是替代了原JPoint,如果要执行原JPoint的话,需要调用proceed

在Android 中AOP工具和库

  • AspectJ: 一个 JavaTM 语言的面向切面编程的无缝扩展(适用Android)。

  • Javassist for Android: 用于字节码操作的知名 java 类库 Javassist 的 Android 平台移植版。

  • DexMaker: Dalvik 虚拟机上,在编译期或者运行时生成代码的 Java API。

  • ASMDEX: 一个类似 ASM 的字节码操作库,运行在Android平台,操作Dex字节码。

在Android 中AOP的使用

这里使用 AspectJ:
* 功能强大
* 支持编译期和加载时代码注入
* 易于使用

先要装ASpectJ环境

AspectJ安装包

其中还要在gradle内配置一些东西,具体详细请看代码,这里就不贴了。

注解类 AspectJAnnotation
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AspectJAnnotation {
    String value();
}
执行类AspectJTest
/**
 * <p/>
 * Author: 温利东 on 2017/3/2 16:07.
 * blog: http://blog.youkuaiyun.com/sinat_15877283
 * github: https://github.com/LidongWen
 */
@Aspect
public class AspectJTest {
    private static final String TAG = "tag00";

    @Pointcut("execution(@com.wenld.aspectjdemo.AspectJAnnotation  * *(..))")
    public void executionAspectJ() {

    }

    @Around("executionAspectJ()")
    public Object aroundAspectJ(ProceedingJoinPoint joinPoint) throws Throwable {
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        Log.i(TAG, "aroundAspectJ(ProceedingJoinPoint joinPoint)");
        AspectJAnnotation aspectJAnnotation = methodSignature.getMethod().getAnnotation(AspectJAnnotation.class);
        String permission = aspectJAnnotation.value();
        if(permission.equals("权限A")) {
           Object result=joinPoint.proceed();
            Log.i(TAG, "有权限:"+permission);
            return result;
        }
        return "";
    }
}
MainActivity
    @AspectJAnnotation(value = "权限A")
    public String test() {
        Log.i(TAG, "检查权限");
        return "test";
    }
 示例源码地址:https://github.com/LidongWen/AspectJDemo/tree/master

* 一些资料地址:*
https://en.wikipedia.org/wiki/AOP
l http://www.eclipse.org/aspectj/ <=AspectJ官方网站
l http://www.eclipse.org/aspectj/doc/released/runtime-api/index.html <=AspectJ类库参考文档,内容非常少
l http://www.eclipse.org/aspectj/doc/released/aspectj5rt-api/index.html <=@AspectJ文档,以后我们用Annotation的方式最多。
AspectJ语法
http://www.eclipse.org/aspectj/doc/released/quick5.pdf 或者官方的另外一个文档也可以:
http://www.eclipse.org/aspectj/doc/released/progguide/semantics.html


各位同学,目前已经转入至简书,欢迎来搞事情
简书地址: http://www.jianshu.com/users/99f514ea81b3/timeline
github: LidongWen
人生得意须尽欢, 桃花坞里桃花庵
点个关注呗,对,不信你点试试?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值