Android使用AspectJ

9,447 阅读4分钟

一、基本介绍

1. AOP

在了解AspectJ前,我们首先了解另外一个名词:AOP

1.1 概念

AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,它可以通过预编译方式和运行期动态代理实现在不修改源代码的情况下给程序动态统一添加功能的一种技术。

1.2 作用

AOP可以做什么呢?举个例子,android中容易发生View被连续点击的情况,正常我们可以在某个view的onClick事件中记录上一次点击的时间点然后进行对比,在1s内的多次点击就被忽略掉。但是,先不说整个应用,某个界面上可点击的view就有那么多,我们要为每一个view的onClick都增加同样的处理逻辑么?AOP就可以帮我们进行统一处理。

1.3 实现方式

AOP实现主要分为 静态 和 动态 两种

  • 静态方式:在编译期,切面直接以字节码方式编译到目标字节码文件中,生成静态的AOP代理类(主要有:AspectJ等)
  • 动态方式:在运行期,为接口动态生成代理类(主要有:Spring AOP、动态代理、自定义类加载器等)

2. AspectJ

理解完AOP,我们再来看AspectJ官方progguide

2.1 概念

AspectJ是一个面向切面的框架,它扩展了Java语言。AspectJ定义了AOP语法,它有一个专门的编译器用来生成遵守Java字节编码规范的Class文件。简单来说,AspectJ是AOP的一种实现框架。

2.2 使用

AspectJ是在编译期通过特殊的编译器(ajc)在不改变代码的前提下织入代码。所以我们有必要了解下ajc官方文档。ajc通俗讲就是AspectJ编译器,在上方文档可以看到,可以进行一系列的参数配置。

二、基本使用

1. 以Gradle的Plugin形式使用

gradle的plugin创建方式也有多种,# Developing Custom Gradle Plugins:

  • Build script
  • buildSrc project
  • Standalone project 后续我会单独一篇文章详细介绍,这里只使用Standalone project的方式介绍AspectJ的使用

1.1 创建Android module:libplugin

创建一个Android Library,然后删除掉不必要的java文件夹、Androidmanifest.xml等文件。由于插件使用groovy编写,所以删掉java,重新创建groovy文件夹。

1.2 修改build.gradle

plugins {
    // 使用的是groovy
    id 'groovy'
}

dependencies {

    implementation gradleApi()//gradle sdk
    implementation localGroovy()//groovy sdk
    implementation 'org.aspectj:aspectjtools:1.8.1'
}


// 用于发布该插件,其他工程可通过配置引用
apply plugin: 'maven-publish'
repositories {
    google()
    jcenter()
    mavenCentral()
}

afterEvaluate {
    publishing {
        publications {
            release(MavenPublication) {
                from components.java
                groupId = 'com.king.plugin'
                artifactId = 'AspectJPlugin'
                version = '1.0.0'
            }
        }

        repositories {
            maven {
                allowInsecureProtocol true
                // 指定发布的位置为当前工程的repo目录下
                url = uri('../repo')
            }
        }
    }
}

1.3 编写插件

继承org.gradle.api.Plugin,在apply编写逻辑

public class AspectJPlugin implements Plugin<Project> {

    @Override
    void apply(Project project) {
        project.dependencies {
            implementation 'org.aspectj:aspectjrt:1.8.9'
        }
        project.android.applicationVariants.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)]

                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;
                    }
                }
            }
        }
    }
}

1.4 创建properties文件,关联插件

具体文件结构如下图所示

微信截图_20211029114658.png

1.5 构建发布

在gradle功能区展开,可以看到libplugin下边有个publish的task,双击运行

微信截图_20211029114756.png

就会在AspectJDemo目录下生成repo文件夹,并生成了相应的插件

微信截图_20211029114812.png

1.5 app module中使用该插件

在项目的build.gradle配置上方生成的插件

buildscript {
    repositories {
        google()
        // 配置插件路径
        maven {
            url('repo')
        }
        mavenCentral()
    }
    dependencies {
        classpath "com.android.tools.build:gradle:7.0.3"
        // 配置插件
        classpath "com.king.plugin:AspectJPlugin:1.0.0"
        classpath "org.aspectj:aspectjrt:1.8.9"
    }
}

allprojects {
    repositories {
        google()
        maven {
            url('repo')
        }
        mavenCentral()
    }
}

在app module 的build.gradle使用该插件

plugins {
    id 'com.android.application'
}
// 使用插件
import com.king.plugin.AspectJPlugin
apply plugin: AspectJPlugin
}

dependencies {
    ...
    implementation 'org.aspectj:aspectjrt:1.8.9'
}

1.6 总结

至此,你已经可以正常的在项目中使用aspectj了~这里主要介绍aspectj的使用,所以不详细介绍上边的禁止view频繁点击demo,具体代码在gitee: Demo下载地址

2. 直接使用别人写好的aspectj plugin

上述的步骤挺繁琐,所以有人专门写好了相应的插件:gradle_plugin_android_aspectjx

主要的使用流程如下:

2.1 项目build.gradle配置

buildscript {
    ...
    dependencies {
        ...
        classpath 'com.hujiang.aspectjx:gradle-android-plugin-aspectjx:2.0.8'
    }
}

2.2 app module的build.gradle配置

apply plugin: 'android-aspectjx'

// 配置需要编译包含的文件
aspectjx {
    include 'com.king'
    // exclude:排除
}

// aspectjx的开关,默认为true
aspectjx {
    enabled true
}

2.3 总结

至此,已经可以正常在项目中使用aspectj了~相比起来,会比上边的方法方便很多。稍后再添加上这个相应的demo