Android aop Advice(通知、增强)

推荐阅读系列文章

Android aop切点表达式(execution)
Android aop(AspectJ)查看新的代理类
Android AOP面向切面编程详解
防止按钮连续点击

AOP AspectJ方案有三要素:

  • @Aspect:定义切面类;
  • @Pointcut:定义切点;
  • Advice(通知/增强):定义一个通知方法,是处理逻辑的。

Advice(通知/增强)方法的作用是:在切点方法执行前、后,甚至是抛出异常的时候去做一些其它逻辑操作。

具体来说分为5种通知:

  • @Before(): 切点方法执行之前执行
  • @After(): 切点方法执行之后执行
  • @Around(): 在切点方法执行前、后都可以做一些操作
  • @AfterReturning():切点方法返回结果之后执行
  • @AfterThrowing():异常通知,切点抛出异常时执行

@Before()

在目标方法执行之前做一些操作,比如说在Activity生命周期方法打印日志。

MainActivity中定义过方法

    /**
     * 测试5种通知/增强
     */
    private void testAdvice(){
        Log.e(TAG, "MainActivity testAdvice(): " );
    }

在切面类中定义如下方法

    @Before("execution(* com.zx.aop1.MainActivity.testAdvice(..))")
    public void testBefore(){
        Log.e(TAG, "testBefore: before testAdvice() method " );
    }

@Before执行结果

@Before.png

@After()

在目标方法执行之后做一些操作,比如说释放资源等。

    @After("execution(* com.zx.aop1.MainActivity.testAdvice(..))")
    public void testAfter(){
        Log.e(TAG, "testAfter: after testAdvice() method " );
    }

@After执行结果

@After.png

@Around()

这种应用场景很多,比如说在做xx操作之前检查权限;还有登录检查;防止按钮重复点击等。

@Around("execution(* com.zx.aop1.MainActivity.testAdvice(..))")
    public void testAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        //在方法执行之前做一些事情
        Log.e(TAG, "testAfter: before testAdvice() method do something" );
        //调用原方法
        proceedingJoinPoint.proceed();
        //在方法执行之后做一些事情
        Log.e(TAG, "testAfter: after testAdvice() method do something" );
    }

这里有点区别,因为是环绕方法,需要手动调用原来的方法,具体来说是proceedingJoinPoint.proceed();``ProceedingJoinPointJoinPoint的子类,这里先不展开讲,后面再详细讲这2个类的作用。

@Around执行结果

Around.png

@AfterReturning()

这和@After类似,都是目标方法执行之后再做些其它操作;区别是
@AfterReturning()可以拿到原方法的返回结果。

 @AfterReturning(value = "execution(* com.zx.aop1.MainActivity.testAdvice(..))",returning = "result")
    public void testAfterReturning(String result){
        Log.e(TAG, "testBefore: AfterReturning testAdvice() method return: "+result );
    }

    /**
     * 测试5种通知/增强
     */
    private String testAdvice(){
        Log.e(TAG, "MainActivity testAdvice() perform: " );
        return "this is testAdvice() result";
    }

这里多了一个returning参数,注意,它的值必须和方法参数的名称一致,@AfterReturning标注的方法参数的类型必须和原方法返回类型一致。

@AfterReturning执行结果

@AfterReturning.png

下面对@AfterReturning()参数做一个详细解释:
源码定义

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface AfterReturning {

    /**
     * The pointcut expression where to bind the advice
     */
    String value() default "";

    /**
     * The pointcut expression where to bind the advice, overrides "value" when specified
     */
    String pointcut() default "";

    /**
     * The name of the argument in the advice signature to bind the returned value to
     */
    String returning() default "";
    
    /**
     * When compiling without debug info, or when interpreting pointcuts at runtime,
     * the names of any arguments used in the advice declaration are not available.
     * Under these circumstances only, it is necessary to provide the arg names in 
     * the annotation - these MUST duplicate the names used in the annotated method.
     * Format is a simple comma-separated list.
     */
    String argNames() default "";

}

源码中定义了4个方法:
其中value()pointcut()功能基本一样:都是定义advice(通知/增强)方法中用到的切点表达式的。一样既可以是已有的切入点,也可直接定义切入点表达式。当指定了pointcut属性值后,value属性值将会被覆盖。通常用其中一个就行了。

returning()可以拿到目标方法的返回值。注意:它的值必须与Advice方法中定义的参数一致。returning()的值是String类型,别误以为返回参数只能是String,返回参数可以是任意类型,这里的值只是参数的名称而已。

@AfterThrowing()

切点抛出异常时执行这个通知方法

    @AfterThrowing(value = "execution(* com.zx.aop1.MainActivity.testException(..))", throwing = "exception")
    public void testAfterThrowing(Exception exception) {
        Log.e(TAG, "testBefore: AfterThrowing testException() method return: " + exception.toString());
//        throw exception;
    }

    /**
     * 测试异常
     */
    private void testException() {
        TextView textView = null;
        textView.setText("hello");
        Log.e(TAG, "MainActivity testException() perform: ");
    }

testException ()方法里面会抛出一个空指针异常,然后在testAfterThrowing()会执行。它与try catch的方式是不太一样的,try catch可以完全处理异常,但是@AfterThrowing却做不到,它只能在异常抛出之后做一些其它的逻辑处理,但是处理后异常仍然会往上一级调用者传播

另外需要注意:如果目标方法中出现异常,并由catch捕捉处理且catch又没有抛出新的异常,那么针对该目标方法的AfterThrowing增强处理将不会被执行。

结论:我目前的感觉是这个通知注解没什么用。先做个了解,后面再看看。

JoinPoint和ProceedingJoinPoint

JoinPoint

JoinPoint其实就是封装了切点方法相关的信息,

    @Before("execution1()")
    public void testExecution(JoinPoint joinPoint){
        Log.e(TAG, "testExecution ----: "+joinPoint.toString() );
    }

JoinPoint方法详解

public interface JoinPoint {  
   String toString();         //连接点相关信息  
   Object getThis();         //返回AOP代理对象  
   Object getTarget();       //返回目标对象  
   Object[] getArgs();       //返回切点方法参数列表  
   Signature getSignature();  //返回当前连接点签名  
   SourceLocation getSourceLocation();//返回连接点方法所在类文件中的位置  
   String getKind();        //连接点类型  
   StaticPart getStaticPart(); //返回连接点静态部分  
  }  
ProceedingJoinPoint

ProceedingJoinPointJoinPoint的子接口,该对象只用在@Around的切面方法中

@Around("execution(* *..MainActivity.testAOP())")
    public void onActivityMethodAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        String key = proceedingJoinPoint.getSignature().toString();
        Log.e(TAG, "onActivityMethodAroundFirst: before " + key + "do something");
        //执行原方法
        proceedingJoinPoint.proceed();
        Log.e(TAG, "onActivityMethodAroundSecond: after " + key + "do something");
    }

关键点在于proceed()方法,有没有发现,这个流程不就是代理模式吗?

方法详解

/**
 * ProceedingJoinPoint exposes the proceed(..) method in order to support around advice in @AJ aspects
 *
 * @author <a href="mailto:alex AT gnilux DOT com">Alexandre Vasseur</a>
 */
public interface ProceedingJoinPoint extends JoinPoint {

    /**
     * 执行目标方法 
     *
     * @return
     * @throws Throwable
     */
    public Object proceed() throws Throwable;

    /**
     * 传入的新的参数去执行目标方法 
     *
     * @param args
     * @return
     * @throws Throwable
     */
    public Object proceed(Object[] args) throws Throwable;

}

写文不易,如果本文对你有所帮助,请点个关注

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 210,914评论 6 490
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 89,935评论 2 383
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 156,531评论 0 345
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,309评论 1 282
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,381评论 5 384
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,730评论 1 289
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,882评论 3 404
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,643评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,095评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,448评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,566评论 1 339
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,253评论 4 328
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,829评论 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,715评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,945评论 1 264
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,248评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,440评论 2 348

推荐阅读更多精彩内容