AOP(Aspect-Oriented Program)面向切面编程,它是通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。比如,我们可以通过它来拦截一些种类的方法。以点击事件为例,我们可以在点击事件处理回调之前,切一个切点,关注下它的网络数据请求情况,即检测一下当前网络是否可用,然后也可以切另外一个切点,关注下它的方法执行时长。而这个方法就是一个切面,这类方法可以有很多个,只要符合相同的特性,比如我们上面提到的点击事件处理。
我们实现AOP通常最直接的方式就是使用AspectJ,当然了,只要你技术足够到位,也可以自己用动态代理模拟。我们可以定义一个gradle插件,在编译的时候辅助编译。
import org.aspectj.bridge.IMessage
import org.aspectj.bridge.MessageHandler
import org.aspectj.tools.ajc.Main
import org.gradle.api.Plugin
import org.gradle.api.Project
/**
* @使用ajc编译java代码 , 同 时 织 入 切 片 代 码
* 使用 AspectJ 的编译器(ajc,一个java编译器的扩展)
* 对所有受 aspect 影响的类进行织入。
* 在 gradle 的编译 task 中增加额外配置,使之能正确编译运行。
*/
class AspectjPlugin implements Plugin<Project> {
void apply(Project project) {
project.dependencies {
api 'org.aspectj:aspectjrt:1.8.10'
}
final def log = project.logger
log.error "============aspectj start============"
def hasApp = project.plugins.hasPlugin("com.android.application")
final def variants
if (hasApp) {
variants = project.android.applicationVariants
} else {
variants = project.android.libraryVariants
}
variants.all { variant ->
def 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.error "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
}
}
}
}
log.error "============aspectj stop============"
}
}
然后以重复点击防抖为例。先声明一个注解。
@Target(AnnotationTarget.FUNCTION)
annotation class AopOnClick(
/**
* 点击间隔时间
*/
val value: Long = 1000)
然后定义一个切面实现。
import org.aspectj.lang.ProceedingJoinPoint
import org.aspectj.lang.annotation.Around
import org.aspectj.lang.annotation.Aspect
import org.aspectj.lang.annotation.Pointcut
@Aspect
class AopClickAspect {
/**
* 定义切点,标记切点为所有被@AopOnclick注解的方法
* 注意:这里com.example.annotation.AopOnClickk需要替换成
* 你自己项目中AopOnclick这个类的全路径
*/
@Pointcut("execution(@com.example.annotation.AopOnClick * *(..))")
fun methodAnnotated() {
}
/**
* 定义一个切面方法,包裹切点方法
*/
@Around("methodAnnotated()")
@Throws(Throwable::class)
fun aroundJoinPoint(joinPoint: ProceedingJoinPoint) {
val methodSignature = joinPoint.signature as MethodSignature
val method = methodSignature.method
if (!method.isAnnotationPresent(AopOnClick::class.java)) {
return
}
val aopOnClick = method.getAnnotation(AopOnClick::class.java)
// 判断是否快速点击
if (!AopClickUtil.isFastDoubleClick(aopOnClick.value)) {
// 不是快速点击,执行原方法
joinPoint.proceed()
}
}
}
用到的工具方法。
object AopClickUtil {
/**
* 最近一次点击的时间
*/
private var mLastClickTime: Long = 0
/**
* 是否是快速点击
*
* @param intervalMillis 时间间期(毫秒)
* @return true:是,false:不是
*/
fun isFastDoubleClick(intervalMillis: Long): Boolean {
// long time = System.currentTimeMillis();
val time = SystemClock.elapsedRealtime()
val timeInterval = Math.abs(time - mLastClickTime)
return if (timeInterval < intervalMillis) {
true
} else {
mLastClickTime = time
false
}
}
}
这样就可以在所有配置了@AopOnClick注解的方法实现AOP重复点击防抖了。
阅读量:1977
点赞量:0
收藏量:0