Android中AOP的应用实践之过滤重复点击
作者:uncochen 发布时间:2022-09-19 22:01:02
前言
大家对AOP应该都不陌生, 就算没有用过也肯定听说过,切面编程一直是一个热点的话题,AOP即Aspect Oriented Programming的缩写,习惯称为切面编程;与OOP(面向对象编程)万物模块化的思想不同,AOP则是将涉及到众多模块的某一类问题进行统一管理,AOP的优点是将业务逻辑与系统化功能高度解耦,让我们在开发过程中可以只专注于业务逻辑,其他一些系统化功能(如路由、日志、权限控制、 * 、埋点、事件防抖等)则由AOP统一处理;
AspectJ简介
AOP是一种编程思想,或者说方 * ,AspectJ则是专为AOP设计的一种语言,它支持原生的JAVA,可用于在java中处理AOP的相关问题;下面非常简单的描述下AspectJ中几个要点
Join Points
AspectJ中的切点,是AspectJ作用到具体某个位置的说明,主要包括三类:
函数(函数调用,函数执行,构造函数等)
变量(变量get,变量set等)
代码块(静态代码块,for等)
Pointcuts
AspectJ中的切面(这种翻译不一定正确),由点及面,用于说明你需要hook哪一类问题,比如我需要hook所有的Activity的生命周期方法,则:
@Pointcut("execution(* android.app.Activity.on*(..))")
advice
Join Points和Pointcuts用来说明需要hook哪些位置或者流程,advice则用于hook之后指定需要做什么,包括:
before()
在切入点之前操作
after()
在切入点之后操作
after():returning
函数正常结束after():throwing
函数异常结束
around()
完全替换函数(可以手动再调用原函数)
around()
用的会比较多,因为自由度高,其他的用around()
都可以实现
AOP处理android中的重复点击
短时间的重复点击如果不做处理会带来不好的体验且可能引发问题(打开多个页面,多次提交,数据错乱),之前我写过一篇文章使用代理模式+反射来处理重复点击的问题:Android-如何优雅的处理重复点击 ,虽然这种方式能达到目的且还算灵活,但还是存在侵入性,对于业务逻辑不是完全透明,所以我们需要使用跟好的方式来处理;
AOP用于处理某一类独立的问题,非常契合屏蔽重复点击的需求,我们只需要hook住原先的点击事件(转确的说是点击事件后的处理流程),判断是不是重复点击,是则过滤掉不让它执行,否则就正常执行;
代码
在Android中进行AspectJ的实现,建议使用Hujiang大神的框架gradle_plugin_android_aspectjx,可以非常方便的集成和配置AspectJ在Android中的环境
集成
//root gradle
dependencies {
classpath 'com.hujiang.aspectjx:gradle-android-plugin-aspectjx:2.0.1'
}
//app或module gradle
apply plugin: 'android-aspectjx' //插件
compile 'org.aspectj:aspectjrt:1.8.9' //jar
AspectJ代码
@Aspect
public class ClickFilterHook {
private static Long sLastclick = 0L;
private static final Long FILTER_TIMEM = 1000L;
@Around("execution(* android.view.View.OnClickListener.onClick(..))")
public void clickFilterHook(ProceedingJoinPoint joinPoint) {
if (System.currentTimeMillis() - sLastclick >= FILTER_TIMEM) {
sLastclick = System.currentTimeMillis();
try {
joinPoint.proceed();
} catch (Throwable throwable) {
throwable.printStackTrace();
}
} else {
Log.e("ClickFilterHook", "重复点击,已过滤");
}
}
}
测试
//普通方式 ok
mBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(MainActivity.this,"有效点击",Toast.LENGTH_SHORT).show();
}
});
//butterknife等IOC框架 ok
@OnClick({R.id.btn})
public void onViewClicked(View view) {
switch (view.getId()) {
case R.id.btn:
Toast.makeText(MainActivity.this,"有效点击",Toast.LENGTH_SHORT).show();
break;
}
}
//自定义view ok
@BindView(R.id.tv_small_up)
StrokeTextView mTvSmallUp;
...
mTvSmallUp.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(MainActivity.this,"有效点击",Toast.LENGTH_SHORT).show();
}
});
可以发现,我们处理重复点击的代码,对于原先的代码是没有任何耦合的,对于业务逻辑是完全透明,甚至业务逻辑代码里都没有体现,这一类问题就已经被处理好了,而且是全局的处理;
说一下上面的代码中几个点:
1、@Aspect:该注解用于标注使用Aspect的类,即你编写Aspec代码的类
2、@Around("...")
3、@Around注解用于标注hook之后的处理代码,我们这里使用Around是因为原函数(onClick)可能执行,也可能不执行;注解中的参数则对应Pointcuts
"execution(* android.view.View.OnClickListener.onClick(..))"
对应Pointcuts,即用一个类似正则表达式来告诉控制器你需要hook哪些函数(方法)execution:表示hook的流程是函数执行过程(Join Points有很多种,execution只是其中一种,具体可参见AspectJ官方文档)
android.view.View.OnClickListener.onClick(..))
:表示android.view.View.OnClickListener
该类(或接口)下的所有名为onClick,参数个数未知,参数类型未知的函数
总结
我们通过面向切面思想来过滤掉了重复点击的事件,且高度解耦,可以看到代码非常简单,AOP重在理解这种思想且找准切入点;AOP在Android中还可以有非常多的应用,如:
Android API23+的权限控制
无痕埋点
全局是否登录流程控制
路由控制
日志系统
事件防抖(重复点击)
...
后面有机会再聊这些应用;文章如有任何描述不正确或欠妥的地方,还请大家务必提出来我及时改正,免得误导更多盆友;
参考:深入理解Android之AOP
来源:https://www.jianshu.com/p/483d6ae21322


猜你喜欢
- 本文实例为大家分享了Unity实现每日签到系统的具体代码,供大家参考,具体内容如下代码:using System;using System.
- 本文实例讲述了Android开发中ProgressDialog简单用法。分享给大家供大家参考,具体如下:网上一般对进度条的示例都是如何显示,
- 一、代码结构:二、数据实体类:using System;using System.Collections.Generic;using Sys
- idea工具使用 Java Exception Breakpoint 添加异常断点,在IDE里,新建一个断点,类型是Java Excepti
- 本文实例为大家分享了Java实现猜数字游戏的具体代码,供大家参考,具体内容如下完成猜数字游戏需要实现以下几点:获得一个随机数作为“答案数”;
- 背景在一些业务场景, 往往需要自定义异常来满足特定的业务, 主流用法是在catch里抛出异常, 例如:public void deal()
- 前言:今天修改项目中一个有关WebView使用的bug,激起了我总结WebView的动机,今天抽空做个总结。简介WebView是一个基于we
- 在进行Java打印输出,进行查看字段值的时候,觉得每次写了System.out.println之后,正式发布的时候,还得一个个的删掉,太麻烦
- RocketMQ发送消息我们在使用RocketMQ发送消息时,一般都会使用DefaultMQProducer,类型的代码如下:Default
- 使用Unity API PlayerBuildInterface.CompilePlayerScripts 将项目中的代码生成为 DLL 程
- 前言:在 Spring 中, IOC 是很重要的概念,其本质就是 map 结构,存储容器和业务 Be
- 类与对象:类是抽象的数据类型,对象是抽象的数据类型的具体化。使用new 关键字创建对象,默认初始化为null一个项目只存在一个main方法,
- 字符串每隔4位加空格今天弄了个银行卡识别功能,回显的时候想要将银行卡号每四位加一个空格,这样核对卡号会方便很多,这里记录一下1.正则表达式实
- 在 Eclipse 里新建好工程后,默认会有一个assets目录,在 Eclipse 中直接将准备好的 SQLite 数据库复制到该目录中,
- 摘要:1.日志输出到文件并根据LEVEL级别将日志分类保存到不同文件2.通过异步输出日志减少磁盘IO提高性能3.异步输出日志的原理1、配置文
- 本文实例为大家分享了java实现顺时针打印矩阵的具体代码,供大家参考,具体内容如下题目:输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每
- 前言我们很多小伙伴平时都是做JAVA开发的,那么作为一名合格的工程师,你是否有仔细的思考过JVM的运行原理呢。如果懂得了JVM的运行原理和内
- 我计划在后续的一段时间内,写一系列关于java 9的文章,虽然java 9 不像Java 8或者Java 11那样的核心java版本,但是还
- java.lang.NoSuchMethodException: com.sun.proxy.$Proxy58.list错误解决办法玩web
- 概述Sentinel 支持对 Spring Cloud Gateway、Zuul 等主流的 API Gateway 进行限流。Sentine