通过自定义gradle插件来实现基于字节码插桩的AOP功能
背景
大家都知道android studio一直以来都是依靠gradle来进行项目构建。那么在构建的过程当中是否提供了aop的切面,来暴露给我们使用呢?答案是肯定的。我们可以通过gradle配合,来完成自己的biheaver自定义插件。
那么又有人会问,如果真的要使用·aop的切面功能,直接使用诸如biheaver这样的第三方库不就可以了么?在大部分情况下确实可以满足需求,但是它们的使用场景还是有很大的局限性的。那就是,我们需要在添加apply plugin: 'com.android.application’的build.gradle当中才可以使用。也就是说,诸如biheaver这样的第三方库,它们的使用场景都是app当中,这显然是不符合一些sdk商家需求的,所以我们需要自己动手来解决这个问题。
AOP功能实现基础
关于gradle
首先,我们此次既然是基于自定义gradle插件实现的AOP功能,我们就用groovy语言来实现这一功能。而编写gradle插件主要有两种。
第一种:直接在build.gradle中进行编写,这样做显然是只能在自己的项目中使用的,因此我们抛弃这种方式。
第二种:就是我们今天主要来描述的方式,就是在一个独立的Module中去实现这一功能。这样,我们可以将其编译成maven库,或者jar包,来提供给第三方进行灵活使用。
关于字节码插桩
在android编译的过程当中,都会生成相应字节码,而我们就是通过修改其字节码完成插桩功能。为此,我们需要一些帮助。本文中使用ASM框架来帮助我们进行字节码插桩。ASM框架要求我们掌握一定的字节码知识和技巧,本文会在后面描述部分的功能实现。想要知道更多的信息,我也会贴出ASM4的pdf文档,供大家参考使用。
自定义gradle插件的实现
创建Gralde插件的基本过程
1. Gradle插件使用的开发工具。
首先,我们所使用的工具就是android studio。as本身是基于IntelliJ IDEA的基础上发展而来的,再加上我们通常在android项目中使用自定义gradle,必然伴随一些android原生module的代码注入,所以使用这个工具是比较好的选择。
2. Gradle插件Module的创建过程。
这里我们直接创建一个android module,因为as中并没有直接提供给我们一个创建gradle插件的方法。但是并没有什么关系,我们只需要删除用不着的目录,再修改build.gradle就可以了。
在创建完module之后,我们需要删除大部分的文件夹,就剩一个build.gradle,然后创建groovy文件夹用来存放groovy代码,再创建一个resources文件夹。这个文件夹是什么用处,稍后会有详细介绍。
在经过上述处理之后,我们的工程已经变成了如下图所示:
(请忽略.gitignore文件)
3. resources文件夹
在完成以上步骤之后,我们需要在resources文件夹下创建一个几个新的文件夹和文件,最后的目录如下图所示:
这个properties类型的文件,就是我们gradle插件的配置文件。它指定了我们gradle插件的入口groovy类。这里面,其实我们只需要配置一句话即可,就像这样:
implementation-class=com.lian.plugin.xxxPlugin
4. build.gradle文件
接下来,我们看一下我们的build.gradle文件,这个文件主要有两个功能,一个是类似于我们android工程的build.gradle文件的功能,指定各种依赖库;另一个,我们需要打包我们的gradle插件,提供给android项目使用。
讲到第一部分,我们直接在下面贴上这部分的代码:
apply plugin: 'maven'
apply plugin: 'groovy'
compileGroovy {
sourceCompatibility = 1.7
targetCompatibility = 1.7
options.encoding = "UTF-8"
}
repositories {
jcenter()
mavenCentral()
}
dependencies {
compile gradleApi()
compile localGroovy()
compile 'com.android.tools.build:gradle:2.1.3'
compile 'org.ow2.asm:asm:5.0.3'
compile 'org.ow2.asm:asm-commons:5.0.3'
}
这一部分主要就是添加了我们的依赖库,其中就包含了gradleApi,LocalGroovy,gradle插件和asm库这些必要元素。
接下来,我们谈谈第二部分,这一部分,其实作用就是打包,我们也直接贴上这部分的代码:
group = 'com.lianapm.plugin'
version = '1.0.1'
uploadArchives {
version = version + '-SNAPSHOT'//if you are testing the demo I provide locally, you can uncomment this.
repositories {
mavenDeployer {
snapshotRepository(url: uri('../snapshotRepo'))
}
}
}
在加上如上的代码之后,