美文网首页
AOP-AspectJ 实现防重复点击

AOP-AspectJ 实现防重复点击

作者: 一个追寻者的故事 | 来源:发表于2020-04-10 16:41 被阅读0次

一、概念介绍

我们要实现项目中点击事件的防重复点击功能,AOP思想可以很好的实践。业务代码和其它代码可以完全解耦。 使用 aspectJ 来完成这一次的实践。

二、实践

2.1 环境配置
2.1.1 Gradle配置(复杂版)

项目根目录build.gradle进行配置,添加mavenCentral()仓库,并配置 aspectjtoolsaspectjweaver 的版本

buildscript {
    ext.kotlin_version = '1.3.50'
    repositories {
        google()
        jcenter()
        mavenCentral()
        
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:3.5.1'
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"

        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
        classpath 'org.aspectj:aspectjtools:1.9.1'
        classpath 'org.aspectj:aspectjweaver:1.9.1'
    }
}

allprojects {
    repositories {
        google()
        jcenter()
        
    }
}

task clean(type: Delete) {
    delete rootProject.buildDir
}

再将aspectJ的配置拷贝到根目录build.gradle,在这里暴露一个模块结构类型的入参

/**
 *  AspectJ 的配置
 */
import org.aspectj.bridge.IMessage
import org.aspectj.bridge.MessageHandler
import org.aspectj.tools.ajc.Main

def aop(variants) {
    def log = project.logger
    variants.all { variant ->
        if (!variant.buildType.isDebuggable()) {
            log.debug("Skipping non-debuggable build type '${variant.buildType.name}'.")
            return
        }

        JavaCompile javaCompile = variant.javaCompile
        javaCompile.doLast {
            String[] args = ["-showWeaveInfo",
                             "-1.8",
                             "-inpath", javaCompile.destinationDir.toString(),
                             "-aspectpath", javaCompile.classpath.asPath,
                             "-d", javaCompile.destinationDir.toString(),
                             "-classpath", javaCompile.classpath.asPath,
                             "-bootclasspath", project.android.bootClasspath.join(File.pathSeparator)]
            log.debug "ajc args: " + Arrays.toString(args)

            MessageHandler handler = new MessageHandler(true)
            new Main().run(args, handler)
            for (IMessage message : handler.getMessages(null, true)) {
                switch (message.getKind()) {
                    case IMessage.ABORT:
                    case IMessage.ERROR:
                    case IMessage.FAIL:
                        log.error message.message, message.thrown
                        break
                    case IMessage.WARNING:
                        log.warn message.message, message.thrown
                        break
                    case IMessage.INFO:
                        log.info message.message, message.thrown
                        break
                    case IMessage.DEBUG:
                        log.debug message.message, message.thrown
                        break
                }
            }
        }
    }
}

再前往需要的模块去配置,主模块的build.gradle:

//aspectJ 配置
rootProject.aop(project.android.applicationVariants)

同时 dependencies里添加 aspectjrt版本 'org.aspectj:aspectjrt:1.9.1'

//aspectJ 配置
implementation 'org.aspectj:aspectjrt:1.9.1'

如果要在其它Moudle的 build.gradle 里进行配置,如下:

rootProject.aop(project.android.libraryVariants)

然后在dependencies里再添加一遍。

2.1.1 Gradle配置(精简版)

如果使用AspectJ,需要在项目的build里面进行一大丢配置,这里为了方便快捷,推荐使用沪江的gradle_plugin_android_aspectjx。(原理是:通过Gradle Plugin的方式 将公共的功能可以抽取出来成为插件,可以供多个 Module (build.gradle)使用,增加复用性。 其中包括 aspectJ 复杂的gradle配置,都集成在里边了)

1、按照github使用指南,添加依赖。

根目录的build.gradle

buildscript {
    ...
    dependencies {
        classpath 'com.android.tools.build:gradle:3.0.1'
        //添加依赖,如果studio是3.0以上版本,建议使用v1.1.1
        classpath 'com.hujiang.aspectjx:gradle-android-plugin-aspectjx:1.1.1'
        //注意不能少了aspectjtools的依赖,否则会报错
        //同样aspectjtools可以不写在这里,写在app主module的dependencies下面。
    }
}

allprojects {
    ...
}

app 主目录中的build.gradle

apply plugin: 'com.hujiang.android-aspectjx'

android {
    ...
}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    ...
    //aspectjrt的依赖
    implementation 'org.aspectj:aspectjrt:1.8.13'
}

2.2 业务实现
2.2.1 定义注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface SingleClick {
    long value() default 200;
}
2.2.2 定义切面
@Aspect
public class SingleClickAspect {

    int lastViewId = -1;
    long lastClickTimestamp = 0;

    //切入点:规则
    @Pointcut("execution(@com.jxf.aspectjdemo1.annotation.SingleClick * *(..))")
    public void clickFun(){ }

    // 连接点
    @Around("clickFun()")
    public void handleClick(ProceedingJoinPoint joinPoint) throws Throwable{

        //取click方法中的参数
        if(joinPoint.getArgs() != null
                && joinPoint.getArgs().length > 0
                && joinPoint.getArgs()[0] instanceof View){

            View target = (View) joinPoint.getArgs()[0];

            //签名信息
            MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
            Method method = methodSignature.getMethod();

            if (method.isAnnotationPresent(SingleClick.class)){
                SingleClick singleClickAnnotation = method.getAnnotation(SingleClick.class);
                long nowTime = System.currentTimeMillis();
                if(nowTime - lastClickTimestamp <= singleClickAnnotation.value()
                        && target.getId() == lastViewId){
                    Log.i("AOP", "you click is too fast!");
                }else{
                    // 记住上一次点击的时间戳和View的ID
                    lastViewId = target.getId();
                    lastClickTimestamp = nowTime;

                    //执行click方法
                    joinPoint.proceed();
                }
            }
        }

    }
}

2.2.3 调用
public class MainActivity extends AppCompatActivity {

    int number = 1;
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    @SingleClick
    public void txtClick(View v){
        Log.d("AOP", "----------------"+number++);
    }
}

2.2.4 结果
执行结果
2.2.4 其它

我们看一下 编译过后的 MainActivity.class 的内容

public class MainActivity extends AppCompatActivity {
    int number = 1;

    public MainActivity() {
    }

    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        this.setContentView(2131361820);
    }

    @SingleClick
    public void txtClick(View v) {
        JoinPoint var3 = Factory.makeJP(ajc$tjp_0, this, this, v);
        txtClick_aroundBody1$advice(this, v, var3, SingleClickAspect.aspectOf(), (ProceedingJoinPoint)var3);
    }

    static {
        ajc$preClinit();
    }
}

生成的class文件可以看出,编译生成字节码文件时,可以通过aspectJ 的编译器生成符合java字节码规范的class文件,从而可以达到灵活插入部分代码(Aspect)的目的。

三、其它

其它实现:

方法调用性能检测 : hugo
Android 权限申请 :android_permission_aspectjx

参考:
https://www.jianshu.com/p/f577aec99e17
https://www.jianshu.com/p/16c5e8cdcf08

AspectJ官网
AspectJ Programming Guide
AspectJ Development Environment Guide
AspectJ NoteBook

相关文章

  • AOP-AspectJ 实现防重复点击

    一、概念介绍 我们要实现项目中点击事件的防重复点击功能,AOP思想可以很好的实践。业务代码和其它代码可以完全解耦。...

  • flutter:如何实现防抖,防重复点击

    使用时间差方案,实现防重复点击 使用方式: 缺点:每个防重复点击的事件都需要持有各自的 throttleUtil ...

  • vue防重复点击(指令实现)

    防重复点击或提交,在项目中是必不可少,如果封装成一个组件,但是点击的按钮的样式千差万别,有 和 有 各种实现,很难...

  • vue防重复点击(指令实现)

    快速点击按钮会重复多次调用接口,防止出现这样的情况全局定义,方便调用新建plugins.js 在main.js引用...

  • vue防重复点击(指令实现)

    1.快速点击按钮会重复多次调用接口,防止出现这样的情况 全局定义,方便调用 在main.js中引用 按钮调用直接加...

  • vue防按钮重复点击(指令实现)

    快速点击按钮会重复多次调用接口,防止出现这样的情况 全局定义,方便调用 新建`plugins.js` ``` ex...

  • vue防重复点击(指令实现)[转]

    快速点击按钮会重复多次调用接口,防止出现这样的情况 全局定义,方便调用 新建plugins.js 在main.js...

  • vue防重复点击的几种实现

    前言 项目开发过程中发现,在手速快的情况下,vue的组件点击事件会被连续触发多次。这个问题会产生一些意想不到的bu...

  • ReactNative 防重复点击

    之前用react native做点击事件,会遇到迅速点击多次会触发多个响应,通常我们的解决方法是在自己定义butt...

  • 「React Native」防重复点击

    一、防快速重复点击。 点击按钮后,立马将按钮设置为不可点击,按钮置灰,1.5秒后,重新可以点击。二、防网络请求重复...

网友评论

      本文标题:AOP-AspectJ 实现防重复点击

      本文链接:https://www.haomeiwen.com/subject/bemcmhtx.html