Android AOP编程之双击拦截实现

1、什么是AOP

AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,经过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP能够对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度下降,提升程序的可重用性,同时提升了开发的效率。java

2、AOP 使用场景

主要用在公共业务上,例如:
android

  • 登陆判断
  • 网络判断
  • 权限获取
  • 数据校验
  • 日志输出
  • 性能监控
  • 按钮防抖

3、AOP 基本知识点

  • 通知、加强处理(Advice)
    就是你想要的功能,也就是上说的登陆判断、数据校验等。你给先定义好,而后再想用的地方用一下。包含Aspect的一段处理代码。
  • 链接点(JoinPoint)
    就是容许你通知(Advice)的地方,基本每一个方法的前、后(二者都有也行),或抛出异常是时均可以是链接点。
  • 切入点(Pointcut)
    上面说的链接点的基础上,来定义切入点,你的一个类里,有15个方法,那就有十几个链接点了对吧,可是你并不想在全部方法附件都使用通知(使用叫织入,下面再说),你只是想让其中几个,在调用这几个方法以前、以后或者抛出异常时干点什么,那么就用切入点来定义这几个方法,让切点来筛选链接点,选中那几个你想要的方法。
  • 切面(Aspect)
    切面是通知和切入点的结合。如今发现了吧,没链接点什么事,链接点就是为了让你好理解切点搞出来的,明白这个概念就好了。通知说明了干什么和何时干(何时经过before,after,around等AOP注解就能知道),而切入点说明了在哪干(指定究竟是哪一个方法),这就是一个完整的切面定义。

3、AOP 实现方式

  • AspectJ
    一个 JavaTM 语言的面向切面编程的无缝扩展(适用Android)。
  • Javassist for Android
    用于字节码操做的知名 java 类库 Javassist 的 Android 平台移植版。
  • DexMaker
    Dalvik 虚拟机上,在编译期或者运行时生成代码的 Java API。
  • ASMDEX
    一个相似 ASM 的字节码操做库,运行在Android平台,操做Dex字节码。

4、AOP 注解

  • @Aspect(切面)
    声明切面,标记类
  • @Pointcut(切点表达式)
    定义切点,标记方法
  • @Before(切点表达式)
    前置通知,切点以前执行
  • @Around(切点表达式)
    环绕通知,切点先后执行
  • @After(切点表达式)
    后置通知,切点以后执行
  • @AfterReturning(切点表达式)
    返回通知,切点方法返回结果以后执行
  • @AfterThrowing(切点表达式)
    异常通知,切点抛出异常时执行

4、Android 实现AOP方式之AspectJ

  • 项目添加插件路径
classpath  'com.hujiang.aspectjx:gradle-android-plugin-aspectjx:2.0.4'
复制代码
  • app添加gradle依赖和导入插件
apply plugin: 'android-aspectjx'
复制代码
api 'org.aspectj:aspectjrt:1.8.9'
复制代码

5、双击拦截实现

  • 定义点击注解类
@Target({ElementType.METHOD })
   @Retention(RetentionPolicy.RUNTIME)
   public @interface ClickLimit {
       int value() default 500;
   }
复制代码
  • 定义切面类和功能实现
@Aspect
public class ClickLimitAspect {

   private static final int CHECK_FOR_DEFAULT_TIME = 500;

   private static final String POINTCUT_ON_ANNOTATION =
           "execution(@com.xuetian.xtuikit.click.annotation.ClickLimit * *(..))";

   @Pointcut(POINTCUT_ON_ANNOTATION)
   public void onAnnotationClick(){}

   @Around("onAnnotationClick()")
   public void processJoinPoint(ProceedingJoinPoint joinPoint) throws Throwable {
       try {
           Signature signature = joinPoint.getSignature();
           if (!(signature instanceof MethodSignature)){
               joinPoint.proceed();
               return;
           }
           MethodSignature methodSignature = (MethodSignature) signature;
           Method method = methodSignature.getMethod();
           boolean isHasLimitAnnotation = method.isAnnotationPresent(ClickLimit.class);
           String methodName = method.getName();
           int intervalTime = CHECK_FOR_DEFAULT_TIME;
           if (isHasLimitAnnotation){
               ClickLimit clickLimit = method.getAnnotation(ClickLimit.class);
               int limitTime = clickLimit.value();
               if (limitTime <= 0){
                   joinPoint.proceed();
                   return;
               }
               intervalTime = limitTime;
           }
           Object[] args = joinPoint.getArgs();
           View view = getViewFromArgs(args);
           if (view == null) {
               joinPoint.proceed();
               return;
           }
           Object viewTimeTag =  view.getTag(R.integer.xt_click_limit_tag_view);
           if (viewTimeTag == null){
               proceedAnSetTimeTag(joinPoint, view);
               return;
           }
           long lastClickTime = (long) viewTimeTag;
           if (lastClickTime <= 0){
               proceedAnSetTimeTag(joinPoint, view);
               return;
           }

           if (!canClick(lastClickTime, intervalTime)){
               return;
           }
           proceedAnSetTimeTag(joinPoint, view);
       } catch (Throwable e) {
           e.printStackTrace();
           joinPoint.proceed();
       }
   }

   public void proceedAnSetTimeTag(ProceedingJoinPoint joinPoint, View view) throws Throwable {
       view.setTag(R.integer.xt_click_limit_tag_view, System.currentTimeMillis());
       joinPoint.proceed();
   }

   public View getViewFromArgs(Object[] args) {
       if (args != null && args.length > 0) {
           Object arg = args[0];
           if (arg instanceof View) {
               return (View) arg;
           }
       }
       return null;
   }

   public boolean canClick(long lastClickTime, int intervalTime) {
       long currentTime = System.currentTimeMillis();
       long realIntervalTime  = currentTime - lastClickTime;
       return realIntervalTime >= intervalTime;
   }

}
复制代码
相关文章
相关标签/搜索