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.IMessageimport org.aspectj.bridge.MessageHandlerimport org.aspectj.tools.ajc.Mainbuildscript {    repositories {        mavenCentral()    }    dependencies {        classpath 'org.aspectj:aspectjtools:1.9.2'    }}repositories {    mavenCentral()}final def log = project.loggerfinal def variants = project.android.applicationVariantsvariants.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包,在其包下分别创建annotationaspect, 接着在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类,添加如下代码

@Aspectpublic 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注解就可以了,使用起来是不是特别方便

@SingleClickprivate void login() {...}

在kotlin环境下配置

如果按照上面环境配置在 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 下的环境配置就完成了。使用按照上面即可,感谢大家,我们下篇见~

更多相关文章

  1. Android系统默认Home应用程序(Launcher)的启动过程源代码分析(3)
  2. Android 注解指南
  3. Android建立对话框基本的几种方法
  4. 2种自定义android标题栏titleBar的方法
  5. Android通过chrome插件在线查看Android源代码
  6. 关于repo下载android代码时候错误的处理。
  7. Android Environment 的作用以及常用的方法

随机推荐

  1. 球体上的颜色来描绘价值
  2. 如何在序列化后从查询中更新json数据?
  3. Adaboost算法原理与实践
  4. 如何解除谷歌和Auth0之间的账户联系
  5. 如何安装两个Python 2。x和Python 3。x在
  6. 【Python】keras神经网络识别mnist
  7. python之迭代器和生成器
  8. Python开发环境Wing IDE搜索工具介绍
  9. Mac下MySQL-python安装及EnvironmentErro
  10. 使用pandas read_table读取csv文件