Android中AOP的应用实践之过滤重复点击
前言
大家对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+的权限控制
- 无痕埋点
- 全局是否登录流程控制
- 路由控制
- 日志系统
- 事件防抖(重复点击)
- ...
后面有机会再聊这些应用;文章如有任何描述不正确或欠妥的地方,还请大家务必提出来我及时改正,免得误导更多盆友;
总结
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对的支持。
上一篇: JQuery animate动画应用示例
下一篇: CAD文件中怎么建立图层?