本篇文章是基于AspectJ实现AOP的。
AspectJ实际上是对AOP编程思想的一个实践,AOP虽然是一种思想,但就好像OOP中的Java一样,一些先行者也开发了一套语言来支持AOP。目前用得比较火的就是AspectJ了,它是一种几乎和Java完全一样的语言,而且完全兼容Java。
相关注解
@Aspect:声明切面,标记类
@Pointcut(切点表达式):定义切点,标记方法
@Before(切点表达式):前置通知,切点之前执行
@Around(切点表达式):环绕通知,切点前后执行
@After(切点表达式):后置通知,切点之后执行
@AfterReturning(切点表达式):返回通知,切点方法返回结果之后执行
@AfterThrowing(切点表达式):异常通知,切点抛出异常时执行
切点表达式
execution(<@注解类型模式>? <修饰符模式>? <返回类型模式> <方法名模式>(<参数模式>) <异常模式>?)
下面是一个防止重复点击的示例,刚刚应用到项目中,大家做个参考,支持lambda点击事件
根目录build.gradle中添加依赖
classpath 'com.hujiang.aspectjx:gradle-android-plugin-aspectjx:2.0.10'
在使用的module中的build.gradle中添
apply plugin: 'android-aspectjx'
package com.fusheng.aspectjdemo
import android.util.Log
import android.view.View
import org.aspectj.lang.JoinPoint
import org.aspectj.lang.ProceedingJoinPoint
import org.aspectj.lang.annotation.Around
import org.aspectj.lang.annotation.Aspect
import org.aspectj.lang.annotation.Before
import org.aspectj.lang.annotation.Pointcut
/**
* @Author: lixiaowei
* @CreateDate: 2020/12/1 2:12 PM
* @Description:切面控制点击事件重复问题,如果想让某个点击事件可以重复,
* 可以在方法上添加@EnableDoubleClick注解
*/
@Aspect
class SingleClickAop {
val TAG = "SingleClickAop"
private val MIN_CLICK_DELAY_TIME = 600
private var isDoubleClick: Boolean? = false
private var preInvokerStr: String? = null
private var clickViewKey: Int = -1
companion object {
//切点为OnClickListener.onClick方法的匹配表达式
private const val CLICK_POINTCUTS = "execution(* android.view.View.OnClickListener.onClick(..))"
//切点为lambda表达式的匹配表达式
private const val CLICK_IN_LAMBDA_POINTCUTS = "execution(void *..lambda$*(..))"
}
@Pointcut(CLICK_POINTCUTS)
fun onClickPointcuts() {
}
@Pointcut(CLICK_IN_LAMBDA_POINTCUTS)
fun onClickInLambdaPointcuts() {
}
@Before("execution(@com.fusheng.aspectjdemo.EnableDoubleClick * *(..))")
fun beforeEnableDoubleClick(joinPoint: JoinPoint) {
isDoubleClick = true
}
@Around("onClickPointcuts()||onClickInLambdaPointcuts()")
fun onClickListener(joinPoint: ProceedingJoinPoint) {
if (isDoubleClick == true) {
//如果发现有EnableDoubleClick注解标注,那么直接直接执行方法,之后返回
isDoubleClick = false
joinPoint.proceed()
return
}
val args = joinPoint.args
val view: View? = getViewFromArgs(args)
clickViewKey = view?.id ?: -1
Log.d(TAG, "clickViewKey----${clickViewKey}----")
if (view == null) {
//点击了非控件类型的元素,执行
joinPoint.proceed()
return
}
val lastClickTime = view.getTag(clickViewKey) as Long?
val enableClick = canClick(lastClickTime ?: 0)//是否超过限制时间
Log.d(TAG, "enableClick----${enableClick}----${lastClickTime}----")
if (enableClick) {
joinPoint.proceed()
view.setTag(clickViewKey, System.currentTimeMillis())
preInvokerStr = joinPoint.`this`?.toString() ?: ""
} else {
val isDiffInvoker = preInvokerStr != joinPoint.`this`?.toString() ?: ""//是否是相同的目标,防止OnClickListener嵌套引起的第二层onClick方法不执行问题
//Log.d(TAG, "isSameTarget----${isDiffInvoker}----${preInvokerStr}----${joinPoint.`this`?.toString() ?: ""}----")
if (isDiffInvoker) {
joinPoint.proceed()
}
}
}
/**
* 获取参数中的view
*/
private fun getViewFromArgs(args: Array<Any>?): View? {
if (args.isNullOrEmpty()) {
return null
}
for (e in args) {
if (e is View) {
return e
}
}
return null
}
/**
* 判断是否达到可以点击的时间间隔
* @param lastClickTime
*/
private fun canClick(lastClickTime: Long): Boolean {
Log.d(TAG, "currentTimeMillis----${lastClickTime}----${System.currentTimeMillis() - lastClickTime}----")
return System.currentTimeMillis() - lastClickTime >= MIN_CLICK_DELAY_TIME
}
}
项目地址: github.com/xiaoDongBei…
AOP应用场景
按钮防抖
登陆判断
日志打印
性能监控
权限获取
网络判断
数据校验
修复第三方代码,比如第三方jar包
hook 某些代码为封装过的安全性更好的代码, 比如hook系统toast或者Gson等等