Gradle自定义插件
Android Gradle
Android项目使用 Gradle 作为构建框架,Gradle 又是以Groovy为脚本语言。所以学习Gradle之前需要先熟悉Groovy脚本语言。
Groovy是基于Java语言的脚本语言,所以它的语法和Java非常相似,但是具有比java更好的灵活性。
Android Gradle 的 Project 和 Tasks
这个是Gradle中最重要的两个概念。每次构建(build)至少由一个project构成,一个project 由一到多个task构成。项目结构中的每个build.gradle文件代表一个project,在这编译脚本文件中可以定义一系列的task;task 本质上又是由一组被顺序执行的Action`对象构成,Action其实是一段代码块,类似于Java中的方法。
- Android Gradle 构建生命周期
每次构建的执行本质上执行一系列的Task。某些Task可能依赖其他Task。那些没有依赖的Task总会被最先执行,而且每个Task只会被执行一遍。每次构建的依赖关系是在构建的配置阶段确定的。每次构建分为3个阶段:
- Initialization: 初始化阶段
这是创建Project阶段,构建工具根据每个build.gradle文件创建出一个Project实例。初始化阶段会执行项目根目录下的settings.gradle文件,来分析哪些项目参与构建。
所以这个文件里面的内容经常是:
include ':app'
include ':libraries'
这是告诉Gradle这些项目需要编译,所以我们引入一些开源的项目的时候,需要在这里填上对应的项目名称,来告诉Gradle这些项目需要参与构建。
- Configuration:配置阶段
这个阶段,通过执行构建脚本来为每个project创建并配置Task。配置阶段会去加载所有参与构建的项目的build.gradle文件,会将每个build.gradle文件实例化为一个Gradle的project对象。然后分析project 之间的依赖关系,下载依赖文件,分析project下的task之间的依赖关系。
- Execution:执行阶段
这是Task真正被执行的阶段,Gradle会根据依赖关系决定哪些Task需要被执行,以及执行的先后顺序。
task是Gradle中的最小执行单元,我们所有的构建,编译,打包,debug,test等都是执行了某一个task,一个project可以有多个task,task之间可以互相依赖。例如我有两个task,taskA和taskB,指定taskA依赖taskB,然后执行taskA,这时会先去执行taskB,taskB执行完毕后在执行taskA。
你点击AndroidStudio右侧的一个Gradle按钮,会打开一个面板,内容差不多是这样的:
里面的每一个条目都是一个task,那这些task是哪来的呢?
一个是根目录下的 build.gradle 中的:
dependencies {
classpath 'com.android.tools.build:gradle:3.2.1'
}
一个是 app 目录下的 build.gradle 中的:
apply plugin: 'com.android.application'
这两段代码决定的。也就是说,Gradle提供了一个框架,这个框架有一些运行的机制可以让你完成编译,但是至于怎么编译是由插件决定的。还好Google已经给我们写好了Android对应的Gradle工具,我们使用就可以了。
根目录下的build.gradle中 dependencies { classpath ‘com.android.tools.build:gradle:3.2.1 } 是Android Gradle编译插件的版本。
app目录下的build.gradle中的apply plugin: ‘com.android.application’ 是引入了Android的应用构建项目,还有com.android.library和com.android.application用来构建library和应用。
所有Android构建需要执行的task都封装在工具里,如果你有一些特殊需求的话,也可以自己写一些task。那么对于开发一个Android应用来说,最关键的部分就是如何来用Android Gradle的插件了。
认知Gradle Wrapper
Android Studio中默认会使用 Gradle Wrapper 而不是直接使用Gradle。命令也是使用gradlew而不是gradle。这是因为gradle针对特定的开发环境的构建脚本,新的gradle可能不能兼容旧版的构建环境。为了解决这个问题,使用Gradle Wrapper 来间接使用 gradle。相当于在外边包裹了一个中间层。对开发者来说,直接使用Gradlew 即可,不需要关心 gradle的版本变化。Gradle Wrapper 会负责下载合适的的gradle版本来构建项目。
Android 三个文件重要的 gradle 文件
Gradle项目有3个重要的文件需要深入理解:项目根目录的 build.gradle , settings.gradle 和模块目录的 build.gradle 。
- settings.gradle 文件会在构建的 initialization 阶段被执行,它用于告诉构建系统哪些模块需要包含到构建过程中。对于单模块项目, settings.gradle 文件不是必需的。对于多模块项目,如果没有该文件,构建系统就不能知道该用到哪些模块。
- 项目根目录的 build.gradle 文件用来配置针对所有模块的一些属性。它默认包含2个代码块:buildscript{…}和allprojects{…}。前者用于配置构建脚本所用到的代码库和依赖关系,后者用于定义所有模块需要用到的一些公共属性。
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.2.1'
}
}
allprojects {
repositories {
jcenter()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
(1) buildscript:定义了 Android 编译工具的类路径。repositories中, jCenter是一个著名的 Maven仓库。
(2)allprojects:定义的属性会被应用到所有 moudle中,但是为了保证每个项目的独立性,我们一般不会在这里面操作太多共有的东西。
- 模块级配置文件 build.gradle 针对每个moudle 的配置,这里的定义的选项和顶层
build.gradle定义的相同。它有3个重要的代码块:plugin,android 和 dependencies。
自定义gradle插件
编写Gradle插件主要有三种方法:
- build.gradle脚本中直接使用。这种方式就是直接在Android Studio app moudle的build.gradle中进行插件的编写,优点是不用再上传插件到maven或者其它地方,项目就可以直接使用;缺点也很明显,就是只能在自己的项目中使用,不能复用,这个不是我们今天要说的。
- buildSrc中使用。这种方式需要在项目中新建一个model命名为buildSrc,这个目录就用来存放自定义插件。然后在src/main中建立两个目录,一个就是存放代码的groovy目录,一个是存放自定义插件名称的resources目录。这种定义方式也是只能在我们项目中进行使用,不好复用。
- 独立Module中使用。这种方式就是完全独立开发一个Module,可以随便用。
配置
AS中新建一个Module接着就是配置目录了,和上面说的第二种方式其实一样的,配置好的目录如下,其中groovy目录就是放置代码的地方,resources里面放置的是一个配置文件。在gradle-plugins文件夹下新建一个文件com.example.watson.plugin.properties,文件的名字com.example.watson.plugin就是在使用这个插件的地方需要的名字,比如
apply plugin:'com.example.watson.plugin'
上面配置文件内容, 其实就是一个映射关系,找到我们定义的插件NetworkPlugin,com.example.watson.plugin是NetworkPlugin这个插件类所在的包名。
implementation-class=com.example.watson.plugin.NetworkPlugin
插件实现
环境都配置好了,接下来就是实现NetworkPlugin这个插件了,在groovy文件夹下新建包名并在该名下新建文件NetworkPlugin.groovy。当然groovy也是完全兼容Java的,所以小伙伴们看起来完全无压力啊。
定义插件首先要实现Plugin接口,然后在apply方法中实现插件功能。
class NetworkPlugin implements Plugin<Project>{
@Override
void apply(Project project) {
println "------network plugin begin-------"
project.tasks.create("networkTest", NetworkTask) {
doLast {
println "doLast"
}
}
println "------network plugin end-------"
}
}
为了直观感受,我添加了一个task,也就是networkTest,这个Task就是请求百度首页的网页代码并打印出来。在相同目录下新建文件NetworkTest.groovy,Task的具体工作就是@TaskAction这个注解标注的方法中了。
class NetworkTask extends DefaultTask {
@TaskAction
def testNetwork() {
println "------NetworkTask begin-------"
try {
OkHttpClient mOkHttpClient = new OkHttpClient()
Request request = new Request.Builder().url("http://www.baidu.com/").build()
Call call = mOkHttpClient.newCall(request)
Response mResponse = call.execute()
if (mResponse.isSuccessful()) {
println mResponse.body().string()
}
} catch (Exception e) {
e.printStackTrace()
println e.toString()
}
println "------NetworkTask end-------"
}
}
到这里我们插件的代码工作基本就完成了,剩下最后一个问题,怎么用这个插件呢?当然就是上传Maven等等的仓库中了,这里为了方便我就上传到自己的本地了。
上传插件
上传插件就是在这个Module的build.gradle中写Task了,这里我就上传到本地D盘reponsitory目录下了。
apply plugin: 'groovy'
//使用该插件,才能使用uploadArchives
apply plugin: 'maven'
repositories {
jcenter()
}
dependencies {
//使用gradle sdk
compile gradleApi()
//使用groovy sdk
compile localGroovy()
compile 'com.android.tools.build:gradle:3.0.1'
//网络请求框架
compile 'com.squareup.okhttp3:okhttp:3.7.0'
compile 'com.squareup.okio:okio:1.9.0'
}
sourceCompatibility = "1.8"
targetCompatibility = "1.8"
uploadArchives {
repositories.mavenDeployer {
pom.version = '1.0.0'
pom.artifactId = 'watsonPlugin'
pom.groupId = 'com.example.watson.plugin'
repository(url: "file:///D:/repository/")
}
}
点击下build就能在plugin模块看见我们定义的uploadArchives这个Task了。
点击下这个任务就能在对应的目录下生成这个插件。
使用插件
首先在工程的根目录下的build.gradle文件下添加如下代码。maven仓库就是我们上面的仓库地址,添加依赖,其中com.example.watson.plugin就是groupId,watsonPlugin就是artifactId,1.0.0就是版本号了。
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
google()
jcenter()
maven {
url uri('D:/repository') //add
}
}
dependencies {
classpath 'com.android.tools.build:gradle:3.5.2'
classpath 'com.example.watson.plugin:watsonPlugin:1.0.0' //add
}
}
allprojects {
repositories {
google()
jcenter()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
添加上面代码后Sync一下就能看见在引用Module的Tasks->other下面看到我们的Task了:
到这一步就是大功告成了,点击下这个Task就能执行请求网络和打印日志了。
上一篇: Linux编程中的信号
下一篇: PHP进程间通信-信号