通过AspectJX拦截Android重复点击事件
16lz
2021-01-26
背景
Android 点击一个 View 跳到个页面这个常规事件,有时候用户会用他们单身几十年的手速1秒内戳了那么三四五六七八下,然后这个时候要是用户稍微手机性能差点,一下子就跳出那么两三个同样的 Activity 出来,这种去情况还是挺尴尬的,在这里我们可以用面向切面(AOP)的知识对我们这么一个重复事件进行拦截处理。
AspectJX
AspectJX 是个啥子东西来的?GitHub 上是这么说的
一个基于AspectJ并在此基础上扩展出来可应用于Android开发平台的AOP框架,可作用于java源码,class文件及jar包,同时支持kotlin的应用。
- 插件引用
在项目根目录的 build.gradle 里依赖 AspectJX
dependencies { classpath 'com.hujiang.aspectjx:gradle-android-plugin-aspectjx:2.0.4'}
在 app Module 的 build.gradle 里加入依赖
apply plugin: 'com.android.application'apply plugin: 'com.jakewharton.butterknife'apply plugin: 'android-aspectjx'//这一行 别加错地方了android { ...}
好了 Sync 一下吧 ~
配置有问题就看一下官网吧亲~
顺道说一下我踩到的坑,AspectJX 和阿里推送以及某些推送 SDK 冲突问题。以下是我 Google 到的解决方法。
在当前的 module 的 gradle 文件加入如下
android {//.... 省略}dependencies {//.... 省略}//aspectj AOP 和 阿里推送的冲突问题import org.aspectj.bridge.IMessageimport org.aspectj.bridge.MessageHandlerimport org.aspectj.tools.ajc.Mainfinal def log = project.loggerfinal def variants = project.android.applicationVariantsvariants.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; } } }}
拦截器
我们的拦截器要起到以下作用
- 在一定的时间内(可设置值)的重复点击事件的拦截,针对同一个 view
- 要求在某些情况下不需要拦截此点击事件(例如自定义view 的时候)
以下内容需要一点 AspectJX 和 AOP 的知识,如果无法理解建议先了解上面两个知识点。
首先写下我们的拦截事件
@Aspectpublic class InterceptAgainClickAOP {private final String TAG = this.getClass().getSimpleName();//上次点击的时间 private static Long sLastclick = 0L; //拦截所有两次点击时间间隔小于一秒的点击事件 private static final Long FILTER_TIMEM = 1000L; //上次点击事件View private View lastView; //拦截所有* android.view.View.OnClickListener.onClick(..) 事件 //直接setOnclikListener 拦截点击事件 @Around("execution(* android.view.View.OnClickListener.onClick(..))") public void clickFilterHook(ProceedingJoinPoint joinPoint) throws Throwable { //大于指定时间 if (System.currentTimeMillis() - sLastclick >= FILTER_TIMEM) { doClick(joinPoint); } else { //小于指定秒数 但是不是同一个view 可以点击 或者不过滤点击 if (lastView == null || lastView != (joinPoint).getArgs()[0]) { doClick(joinPoint); } else { //大于指定秒数 且是同一个view Log.e(TAG, "重复点击,已过滤"); } } } //执行原有的 onClick 方法 private void doClick(ProceedingJoinPoint joinPoint) throws Throwable { //判断 view 是否存在 if (joinPoint.getArgs().length == 0) { joinPoint.proceed(); return; } //记录点击的 view(最好加上捕获类型转换异常的try) lastView = (View) (joinPoint).getArgs()[0]; //记录点击事件 sLastclick = System.currentTimeMillis(); //执行点击事件 try { joinPoint.proceed(); } catch (Throwable throwable) { throwable.printStackTrace(); } }}
这个时候已经满足我们上述的第一个要求 : **在一定的时间内(可设置值)的重复点击事件的拦截,针对同一个 view **
接下来我们写一个注解
/** * 标记不需要拦截点击 * * @author Cassie * @date 2019年9月26日 */@Retention(RetentionPolicy.RUNTIME)@Target({ElementType.METHOD, ElementType.TYPE}) public @interface UncheckClick {}
我们利用这个注解去标记不需要过滤的点击事件修改上面 InterceptAgainClickAOP.java 的代码
@Aspectpublic class InterceptAgainClickAOP {private final String TAG = this.getClass().getSimpleName();//上次点击的时间 private static Long sLastclick = 0L; //拦截所有两次点击时间间隔小于一秒的点击事件 private static final Long FILTER_TIMEM = 1000L; //上次点击事件View private View lastView; //---- add content -----//是否过滤点击 默认是 private boolean checkClick = true; //---- add content ----- //拦截所有* android.view.View.OnClickListener.onClick(..) 事件 //直接setOnclikListener 拦截点击事件 @Around("execution(* android.view.View.OnClickListener.onClick(..))") public void clickFilterHook(ProceedingJoinPoint joinPoint) throws Throwable { //大于指定时间 if (System.currentTimeMillis() - sLastclick >= FILTER_TIMEM) { doClick(joinPoint); } else { //---- update content ----- 判断是否需要过滤点击 //小于指定秒数 但是不是同一个view 可以点击 或者不过滤点击 if (!checkClick ||lastView == null || lastView != (joinPoint).getArgs()[0]) { //---- update content ----- 判断是否需要过滤点击 doClick(joinPoint); } else { //大于指定秒数 且是同一个view Log.e(TAG, "重复点击,已过滤"); } } } //执行原有的 onClick 方法 private void doClick(ProceedingJoinPoint joinPoint) throws Throwable { //判断 view 是否存在 if (joinPoint.getArgs().length == 0) { joinPoint.proceed(); return; } //记录点击的 view lastView = (View) (joinPoint).getArgs()[0]; //---- add content ----- //修改默认过滤点击 checkClick = true; //---- add content ----- //记录点击事件 sLastclick = System.currentTimeMillis(); //执行点击事件 try { joinPoint.proceed(); } catch (Throwable throwable) { throwable.printStackTrace(); } } //标志不过滤点击 @Before("execution(@com.cassie.annotation.aop.UncheckClick * *(..))") public void beforeuncheckClcik(JoinPoint joinPoint) throws Throwable { Log.i(TAG, "beforeuncheckClcik"); //修改为不需要过滤点击 checkClick = false; }}
以上就是整个过滤重复点击的 AOP 了,注释应该清楚了就不赘述啦~
更多相关文章
- autoMonkey框架原理与应用(一):Monkey基础知识与测试场景
- Android(安卓)可拖动可点击悬浮窗
- android使用篇(四) 注解依赖注入IOC实现绑定控件
- Android中对闹钟Alarm的事件处理
- android 自定义View(一) View的事件分发与绘制
- Android电子牌外接USB读卡器读取内容模拟键盘事件
- unity游戏开发 发布android 游戏 (一)
- 安装Android(安卓)studio的详细步骤
- 《Android开发艺术探索》之学习笔记(三)View的基础知识