Android中的AOP

来源:互联网 发布:qq会员免费开通软件 编辑:程序博客网 时间:2024/06/06 17:32

说一件开心的事情,我的这篇文章_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-initialization Object在构造函数中做得一些工作。 initialization Object在构造函数中做得工作 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就替代了原JPoint around是替代了原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.csdn.net/sinat_15877283 * github: https://github.com/LidongWen */@Aspectpublic 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

人生得意须尽欢, 桃花坞里桃花庵

点个关注呗,对,不信你点试试?

0 0
原创粉丝点击