前言
为了自己做牛逼插件做准备
内容摘要
- gradle 构建简述和 groovy 语言基础, 包括闭包,依赖人Task
- gradle plugin 实现实例
Android 中的 gradle 初体验
Groovy 在 Android 中无处不在,现在一起去跟它见个面,问声好。
两个概念说一下:
- gradle 是构建系统
- groovy 是这个构建系统使用的语言
Gradle 构建系统的生命周期
- 初始化阶段。读取项目根工程的 setting.gradle 的include 信息,决定需要构建的工程
//setting.gradle
include ':app', ':lib1', ':lib2'
- 配置阶段。执行所有工程的 build.gradle 脚本,创建 Task 任务
- 运行阶段。根据 gradle 命令的 task 名称,执行相关依赖任务
换个角度看 build.gradle
做安卓开发天天接触 build.gradle ,以前当配置文件来看,其实他是类文件,不信?我来翻译一下。
// [1] apply 是一个方法,plugin 是参数,值为 com.android.application
apply plugin: 'com.android.application'
// [2] android 是方法,后面直接跟 {}, 这是个闭包写法
// 闭包是 groovy 独有,对减少代码量有奇效,刚接触会有点懵,可以理解为方法指针
android {
// [3] compileSdkVersion 是参数,值是 28
compileSdkVersion 28
// [4] defaultConfig 是参数,类型比较特殊,也是个闭包
defaultConfig {
applicationId "com.xh.testgroovy"
minSdkVersion 19
targetSdkVersion 28
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
}
//[5] groovy遍历的一种写法 each后面是闭包
android.applicationVariants.each { variant ->
}
闭包
两个例子解释下闭包:
//1. 闭包的使用
def myClosure = {String str -> println str}
myClosure('hellowork')
//2. 多参数闭包的使用
def myClosure2 = {String str, int num -> println str+ num}
myClosure2('多参数闭包的使用', 2)
//输出
> Configure project :app
hellowork
多参数闭包的使用2
gradle task 创建
两个任务创建:
task hello {
// 配置阶段
println "hello"
}
task hello {
doLast {// 运行阶段
println "hello"
}
}
一般性思维,会想到第一种创建,多清晰,但是正常写法都是第二张,那里面肯定有区别,具体区别是什么呢?
但是第一种创建会在配置阶段就输出了,而第二张会在运行阶段执行。
任务依赖 dependsOn
// 3. 任务依赖。dependsOn 依赖,执行B 任务会输入 A,B
task A {
doLast {
println 'A'
}
}
task B {
dependsOn A
doLast{
println 'B'
}
}
- 右边 gradle > Tasks > other 会出现task A 和 B
- 点击 B ,执行输出 A,B
Gradle Plugin 步骤
很多开源三方库,都使用 gradle plugin 来生成一些代码,比如 greenDao 生成 dao。
Android Studio 虽然没有提供新建 gradle plugin 入口,但是用来写 gradle plugin 相当顺滑。
一. 新建 module
推荐选择 Android Library ,因为新建的东西少,删的少
二. 只保留src 文件夹和 build.gradle 文件
删除 module 中大部分内容,只保留 src 文件夹和 build.gradle 文件
三. 插件类 MyPlugin2.groovy
- src/main 文件夹下,新建 groovy 文件夹,新建 plugin 类 MyPlugin2.groovy。
- MyPlugin2.groovy 类的 apply 是插件的入口方法,这里打印日志,并调用 TimeListener
//com.
// 包名
package com.hc.groovy
//导包
import org.gradle.api.Plugin
import org.gradle.api.Project
// 插件,需继承 Plugin
public class MyPlugin2 implements Plugin<Project> {
void apply(Project project) {
println "Hello gradle plugin"
System.out.println("========================");
System.out.println("这是第4个插件!");
System.out.println("========================");
project.gradle.addListener(new TimeListener())
}
}
四. 一个记录 Task 耗时的操作
TimeListener.groovy 类,记录打印每个 task 耗时
package com.hc.groovy
import org.gradle.BuildListener
import org.gradle.BuildResult
import org.gradle.api.Task
import org.gradle.api.execution.TaskExecutionListener
import org.gradle.api.initialization.Settings
import org.gradle.api.invocation.Gradle
import org.gradle.api.tasks.TaskState
import org.gradle.internal.time.Clock
import org.gradle.internal.time.MonotonicClock
class TimeListener implements TaskExecutionListener, BuildListener {
private Clock clock
private times = []
private long tmpMs
@Override
void beforeExecute(Task task) {
//填坑之一,org.gradle.util.Clock() 不可用,看了几个 clock 的类,选了 MonotonicClock,试了下能用,测试代码能用就够
clock = new MonotonicClock()
tmpMs = clock.currentTime
}
@Override
void afterExecute(Task task, TaskState taskState) {
clock = new MonotonicClock()
def ms = clock.currentTime - tmpMs
times.add([ms, task.path])
task.project.logger.warn "${task.path} spend 花费 ${ms}ms"
}
@Override
void buildFinished(BuildResult result) {
println "构建结束 Task spend 花费 time:"
def count = 0
for (time in times) {
if (time[0] >= 50) {
printf "%7sms %s\n", time
}
count += time[0]
}
System.out.println("总耗时:"+count+"ms")
}
@Override
void buildStarted(Gradle gradle) {
println "构建开始喽 buildStarted"
}
@Override
void projectsEvaluated(Gradle gradle) {}
@Override
void projectsLoaded(Gradle gradle) {}
@Override
void settingsEvaluated(Settings settings) {}
}
五. 新建 properties 文件 *
- resources/META-INF/gradle-plugins 三个文件,新建 com.hc.groovy.MyPlugin2.properties 文件
- 文件名就是我们宿主方 apply plugin 命令的参数,之前一直没找到,掉坑很久
//填坑之一, 宿主项目的 apply plugin 命令后面跟的参数一直不知道是啥,后来细读文章,才知道
//知道用的是这个 .properties 后缀的文件名
implementation-class=com.hc.groovy.MyPlugin2
六. 配置 build.gradle 文件
- 引入 groovy 插件, 会去下载 gradle-5.1.1 包。这样就能识别我们写的 groovy 代码,原先 build.gradle 的一些方法也可点击,查看实现
- 上传本地 maven
//[1] groovy 插件
apply plugin: 'groovy'
//[2] 上传本地 maven 库
apply plugin: 'maven'
dependencies {
compile gradleApi()
compile localGroovy()
}
repositories {
mavenCentral()
}
// 生成插件的名称和版本号
group='com.hc.groovy'
version='1.0.0'
// [3] 上传 maven 任务,左边 Tasks 会出现 upload/uploadArchives 任务
// [4] uri('../repo') , 项目路径下的 repo 文件
uploadArchives {
repositories {
mavenDeployer {
repository(url: uri('../repo'))
}
}
}
七. 回顾项目结构并准备测试
到这一步,自定义 gradle 已经写好,而且可以上传本地 maven 库了,可以在项目的 repo w文件下看到生成的插件,下一步就是 app 模块里使用了。
插件项目结构
MyPlugin
├── src/
├── main/
├── groovy
├── com.hc.groovy
├── MyPlugin2.groovy //插件入口类
├── TimeListener
├── resources
├── META-INF.gradle-plugins
├── com.hc.groovy.MyPlugin2.properties //属性,定义对应插件的 groovy 类
├── build.gradle //自定义插件构建脚本
八. 根 build.gradle 配置 classpath
buildscript {
repositories {
..
maven{
//[1] 坑1,这里写 ../repo 发现路径缺少了 testGroovy,就改成完全路径了
// url uri('../repo')
url uri('/Users/hc/AndroidStudioProjects/tmp/testGroovy/repo')
}
}
dependencies {
classpath 'com.android.tools.build:gradle:3.4.0'
...
//[2] 可以看到 com.hc.groovy 是包名,myplugin2 自己定义的 group, 1.0.0 自行定义的版本号
classpath 'com.hc.groovy:myplugin2:1.0.0'
}
}
九. 配置 app 模块 build.gradle
- 执行 clean-> build 命令,可以看到我们的输出
//[1] plugin 模块的 properties 文件
apply plugin: 'com.hc.groovy.MyPlugin2'
推荐阅读
- 如何使用Android Studio开发Gradle插件
- 文档还是官方的好
- IBM developer 的精通 Groovy
- 当然,上面两篇我都没看完,因为量巨多,哈哈。Mark 一下,以后有需要回来看。