Android 面向切面编程 AOP

AOP是Aspect Oriented Programming的缩写,即『面向切面编程』。  优势:无侵入

比如我希望在所有页面启动的时候加一个埋点~ 希望在所有按钮点击的时候加个快速重复点击的判断~等等 这样在项目中同一种类型的所有代码处,

统一加入逻辑处理的方法,却不需要改动原有代码,叫做 面向切面编程 AOP

 

Android实现AOP,可以使用的方案主要有两个(都是基于 aspectJ 的,所以语法都一致):

一个是大神的 github.com/JakeWharton…  (不支持)

一个是沪江的 https://github.com/HujiangTechnology/gradle_plugin_android_aspectjx(支持kotlin  依赖方式 github上很详细)

 

添加依赖:

可参考:https://www.jianshu.com/p/c055df92790c (使用的沪江的)

 

语法说明:

 

更详细的说明:

AOP中AspectJ的一些使用说明

 

例子:

先定义个切面类来拦截点击事件:

import com.yitkeji.wukongkabao.utils.FastClickUtils;
import com.yitkeji.wukongkabao.utils.LogUtil;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;

@Aspect //标注AOP类,表示该类里面是处理切面代码的,固定写法
public class FastClickBlockAspect {

    /**  Around注解 说明见上图
     *   * 表示匹配任意内容
     * execution。方法被调用。
     *  .. 表示匹配任意类型任意数量内容
     */
    @Around("execution(* android.view.View.OnClickListener.onClick(..))")
    public void onClick(ProceedingJoinPoint joinPoint) {
        LogUtil.e("拦截到onClick方法执行");
        try {
            if(FastClickUtils.isNoFastClick()){
                LogUtil.e("没有快速点击,继续执行源代码");
                joinPoint.proceed();//继续往下面走,就会执行拦截的方法,这里为 onClick
            }else {
                //快速点击了  不做处理
                LogUtil.e("快速点击了  不做处理");
            }
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        LogUtil.e("拦截结束");
    }

}

activity中点击事件:

@Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_test);

        findViewById(R.id.btn).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                LogUtil.e("点击了");
            }
        });

    }

当点击按钮时,打印日志:

                       单机  时                                                            连续点击 时

要注意一点around和after两种类型是有冲突的,around和before可以共存,

所以还是建议两种方式,一种before和after配合使用,一种around单独使用。

package com.example.mytest.aop;

import android.util.Log;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;

import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;

/**
 * 要注意一点around和after两种类型是有冲突的,around和before可以共存,
 * 所以还是建议两种方式,一种before和after配合使用,一种around单独使用。
 *
 * 方法参数的获取
 * 自定义注解参数的获取
 *
 * https://blog.csdn.net/niubitianping/article/details/78492054?depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-4&utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-4
 */
@Aspect
public class CheckNetworkAspect {


    /**
     * 对切点的定义
     */
    //@Pointcut("execution(* android.view.View.OnClickListener.onClick(..))")
    public void pointcut() {
    }

    //@Before("pointcut()")
    public void before(JoinPoint point) {
        Log.e("tag", "@Before");
    }

    //@After("pointcut()")
    public void after(JoinPoint point) {
        Log.e("tag", "@After");
    }

    /**
     * 切点方法返回结果之后执行
     *
     * @param point
     * @param returnValue
     */
    //@AfterReturning("pointcut()")
    public void afterReturning(JoinPoint point, Object returnValue) {
        Log.e("tag", "@AfterReturning  returnValue = " + returnValue);
    }

    /**
     * 切点表达式: execution(<修饰符模式>? <返回类型模式> <方法名模式>(<参数模式>) <异常模式>?)
     * Around注解 说明见上图
     * * 表示匹配任意内容
     * execution。方法被调用。
     * .. 表示匹配任意类型任意数量内容
     */
    @Around("execution(* android.view.View.OnClickListener.onClick(..))")
    public void onClick(ProceedingJoinPoint joinPoint) {
        Log.e("tag", "拦截到方法前执行");

        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        String name = signature.getName(); // 方法名:test
        Method method = signature.getMethod(); // 方法:public final void com.example.mytest.aop.AopTestActivity$onCreate$1.onClick(android.view.View)
        Class returnType = signature.getReturnType(); // 返回值类型:void
        Class declaringType = signature.getDeclaringType(); // 方法所在类名:MainActivity
        String[] parameterNames = signature.getParameterNames(); // 参数名:view
        Class[] parameterTypes = signature.getParameterTypes(); // 参数类型:View
        List<Object> args = Arrays.asList(joinPoint.getArgs());//方法参数
        Log.e("tag", "参数 = " + args);

        try {
            joinPoint.proceed();//继续往下面走,就会执行拦截的方法,这里为 onClick
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }

        Log.e("tag", "拦截到方法后执行");
    }


    @Pointcut("execution(@com.example.mytest.aop.CheckNetwork * *(..))")
    public void executionCheckNetwork() {
    }

    @Around("executionCheckNetwork()  && @annotation(checkNetwork)")
    public Object afterReturningTest(ProceedingJoinPoint point, CheckNetwork checkNetwork) throws Throwable {
        Log.e("tag", "@AfterReturning  checkNetwork = " + checkNetwork.value());
        Object result = point.proceed();//被拦截的方法执行后的返回值 无返回值时 为null
        Log.e("tag", "返回值 = " + result);
        return result;
    }
}

 

 

 

可参考:

Android 面向切面编程 AOP 解决连续点击打开重复页面问题 (包含语法说明)

防止按钮连续点击 (包含自定义Pointcuts可以让我们更加精确的切入一个或多个指定的切入点)

AspectJ基本用法

第三方(实际项目例子):https://github.com/AICareless/AopArms

posted @ 2019-02-20 10:31  ts-android  阅读(105)  评论(0编辑  收藏  举报