AOP 大家应该都了解过一点,也就是我们所说的面向切面编程,与之相对应的还有 OOP 面向对象编程、POP 面向过程编程,下面我们就一起学习下在 Android 中 AOP 的环境配置以及怎么使用
在项目根目录下的 build.gradle 配置
buildscript {
repositories {
jcenter()
google()
}
dependencies {
...
classpath 'org.aspectj:aspectjtools:1.9.2'
classpath 'org.aspectj:aspectjweaver:1.9.2'
}
}
然后在 app module 目录下创建 aspectj.gradle,添加如下代码,代码中适配了常见的variant.javaCompile
编译警告
dependencies {
implementation 'org.aspectj:aspectjrt:1.9.2'
}
import org.aspectj.bridge.IMessage
import org.aspectj.bridge.MessageHandler
import org.aspectj.tools.ajc.Main
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath 'org.aspectj:aspectjtools:1.9.2'
}
}
repositories {
mavenCentral()
}
final def log = project.logger
final def variants = project.android.applicationVariants
variants.all { variant ->
if (!variant.buildType.isDebuggable()) {
log.debug("Skipping non-debuggable build type '${variant.buildType.name}'.")
return
}
JavaCompile javaCompile = null
if (variant.hasProperty('javaCompileProvider')) {
TaskProvider provider = variant.javaCompileProvider
javaCompile = provider.get()
} else {
javaCompile = variant.hasProperty('javaCompiler') ? variant.javaCompiler : 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
}
}
}
}
最后在 app module 目录下 build.gradle 中引用
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
}
apply from: "aspectj.gradle"
在 Android 中常见的 AOP 使用有防止按钮多次点击
、检测登陆状态
,方法日志打印
等等,下面我们就来简单实现下防止按钮多次点击的功能
首先创建aop
包,在其包下分别创建annotation
和aspect
, 接着在annotation
包下创建SingleClick
注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface SingleClick {
}
@Retention
有三个值可选
RetentionPolicy.SOURCE: 编译后会被丢弃。
RetentionPolicy.CLASS: 编译后会被保留,但不会在运行的VM中保留。默认值
RetentionPolicy.RUNTIME:编译后会被保留,也会在运行的VM中保留。
@Target
有如下值可选
ElementType.TYPE : 类型
ElementType.FIELD: 字段,包括属性的支持字段。
ElementType.METHOD: 方法
ElementType.PARAMETER: 参数
ElementType.CONSTRUCTOR: 构造函数
ElementType.LOCAL_VARIABLE: 局部变量
ElementType.ANNOTATION_TYPE: 注解类型
ElementType.PACKAGE: 包名
ElementType.TYPE_PARAMETER: 参数 java1.8开始
ElementType.TYPE_USE: 使用的类型 java1.8开始
在aspect
包下创建SingleClickAspect
类,添加如下代码
@Aspect
public class SingleClickAspect {
private static int MIN_CLICK_DELAY_TIME = 600;
private static long lastClickTime = 0L;
//@Pointcut来标识所要寻找的切点,就是我们定义的@SingleClick注解
@Pointcut("execution(@com.soaic.helloaspect.aop.annotation.SingleClick * *(..))")//方法切入点
private void methodAnnotated() {}
/**
* joinPoint.proceed() 执行注解所标识的代码
* @After 可以在方法前插入代码
* @Before 可以在方法后插入代码
* @Around 可以在方法前后各插入代码
*/
@Around("methodAnnotated()")
private void aroundJoinPoint(ProceedingJoinPoint joinPoint) throws Throwable{
//获取系统当前时间
long currentTime = Calendar.getInstance().getTimeInMillis();
//当前时间-上次记录时间>过滤的时间 过滤掉600毫秒内的连续点击
//表示该方法可以执行
if (currentTime - lastClickTime > MIN_CLICK_DELAY_TIME) {
//将刚进入方法的时间赋值给上次点击时间
lastClickTime = currentTime;
//执行原方法
joinPoint.proceed();
}
}
}
最后在按钮点击事件的方法里面声明@SingleClick
注解就可以了,使用起来是不是特别方便
@SingleClick
private void login() {
...
}
如果按照上面环境配置在 kotlin 里是不起作用的, 我们这里使用 github 开源的一个插件
GitHub地址: https://github.com/HujiangTechnology/gradle_plugin_android_aspectjx
使用如下:
同样是在项目根目录下的 build.gradle 配置
buildscript {
ext.kotlin_version = '1.3.41'
repositories {
google()
jcenter()
}
dependencies {
...
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath 'com.hujiang.aspectjx:gradle-android-plugin-aspectjx:2.0.4'
classpath 'org.aspectj:aspectjtools:1.9.2'
}
}
然后在 app module 下的 build.gradle 中配置
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'android-aspectjx'
//或者这样也可以
//apply plugin: 'com.hujiang.android-aspectjx'
android {
...
aspectjx {
//排除所有package路径中包含`android.support`的class文件及库(jar文件)
exclude 'android.support'
}
}
dependencies {
...
implementation 'org.aspectj:aspectjrt:1.9.2'
}
至此在 kotlin 下的环境配置就完成了。使用按照上面即可,感谢大家,我们下篇见~