sourceset
很多人可能会觉得奇怪,为什么gradle知道去 src main java 这个路径下去找源码编译,去res路径下找资源编译。其实这些东西都是在sourceset里面默认设置好的, 那么我们怎么知道这个sourceSet有哪些配置是可以修改的呢?当然只有去看源码了。因为我们是android工程 所以我们只要看android的sourceset就可以了。
其实看看这个结构也就知道的差不多了。
再看看 我们一般的资源目录,有的时候我们会觉得这样的资源目录是不够的,为啥?当你的项目有了规模以后 所有模块的资源文件都写在一起,找起来真的很难找。 但是有了sourceset 来优化这个东西就变的简单了
我们新建2个文件夹 一个叫shop 一个叫forum 可以看出来 他们的图标和上面的res的图标是不同的,因为此时gradle还是仅仅将他们当作一个普通文件夹 而不是当作一个资源文件夹。 下面我们就要修改这个配置了。
sourceSets {
main {
resources.srcDirs(['src/main/res', 'src/main/res-forum', 'src/main/res-shop'])
}
}
然后再看一下:
已经可以看到 我们新建的文件夹 已经被识别成是res的路径了。 有了他 我们就可以将我们的资源文件 也像java源代码一样 有了类似于package的效果。
plugin
当我们像把我们写的gradle 代码打包给别人使用的时候 就需要plugin了,其实这个plugin你就理解成是一个特殊的jar包就行。 就跟我们写sdk 是一样的,
基本目录结构就是这样 你甚至可以直接写java 代码 ,比groovy 更容易上手一些。
plugin 如何与使用者通信
可以定义一个实体类 用于接收参数
package com.myplugin;
public class MyExtension {
public String getVersionName() {
return versionName;
}
public void setVersionName(String versionName) {
this.versionName = versionName;
}
public String getVersionCode() {
return versionCode;
}
public void setVersionCode(String versionCode) {
this.versionCode = versionCode;
}
private String versionName;
private String versionCode;
@Override
public String toString() {
return "MyExtension{" +
"versionName='" + versionName + '\'' +
", versionCode='" + versionCode + '\'' +
'}';
}
}
然后定义一个task 用于打印我们接受到的参数
package com.myplugin;
import org.gradle.api.DefaultTask;
import org.gradle.api.tasks.TaskAction;
public class MyTask extends DefaultTask {
public MyTask() {
setGroup("other2");
}
@TaskAction
private void doSth() {
System.out.println("info:" + getProject().getExtensions().findByName("pluginExtension").toString());
}
}
最后不要忘记去plugin中注册我们的扩展和task
package com.myplugin;
import org.gradle.api.Plugin;
import org.gradle.api.Project;
public class MyGradlePlugin implements Plugin<Project> {
@Override
public void apply(Project project) {
project.getExtensions().create("pluginExtension", MyExtension.class);
project.getTasks().create("asdasda", MyTask.class);
}
}
处理好这些之后 我们点一下sync 同步,就可以在右边的列表看到我们的task了
看下使用的时候怎么传值: 去我们的app project下 使用一下这个plugin
最后看下执行结果
groovy 基础
groovy的闭包 还是需要好好理解的,这对你一开始学习gradle很有帮助,虽然本质上来说 你用java也可以写gradle的plugin,但是 github上大部分的 gradle plugin 使用的 主要语言还是groovy,比如tinker, 比如matrix 比如arouter 等等。
闭包的基本使用
static void main(String[] args) {
def clouser = { println("hello world") }
//下面这2种方法都一样
clouser.call()
//这个方法 更加简洁
clouser()
//如何设置闭包的参数
def c2 = { String name, int age ->
println("hello ${name} ,you are ${age}")
}
c2("wuyue", 18)
// 闭包的默认参数 就是it
def c3 = {
println("hello ${it}")
}
c3("burning")
// 闭包的 最后一行 一般就是闭包的返回值
// 甚至可以省略一个return 关键字
def c4 = {
"hello ${it}"
}
println(c4("xxx"))
}
来看下闭包的具体应用,其实就是理解好 闭包的本质就是将一个函数 作为一个参数传递到另外一个函数里面。理解到这个程度就可以了。
比如说这里有个求阶乘的写法
//求阶乘,我们一眼就看出来 这个求阶乘的写法 少了一个循环
int fab(int number) {
int result = 1
1.upto(number, { num -> result = result * num })
return result
}
我们可以到这个upto方法里面看看
第三个参数 就是闭包了, 但是在函数体内部,我们可以发现 这个闭包 被循环调用了。 调用的次数 就是 阶乘的 1~5
所以大家就把闭包 理解成一个 特殊的函数即可,这个函数是可以作为参数 传递到另外一个函数里面的。
大家在学习groovy 或者看其他gradle plugin 源码的时候 有发现 闭包调用的时候 闭包参数不知道怎么传的,只要点到方法里面 看一眼,就知道 这个闭包 需要什么类型的参数,需要几个参数了。
再看一个例子,找出字符串里面 第一个是数字类型的字符
println("wuyue2".find {
it.isNumber()
})
知道这段代码的意思并不难,难的是你要怎么才能理解 find这个函数 传进去的闭包 为什么要这么写?一定学会看闭包的参数,否则 gradle 你是肯定学不好的。
看这个图,里面是个dowhile循环, while 里面调用的 恰好就是我们的闭包,前面我们说过闭包是一个函数, 这里的while循环要成立, 首先就得保证 这个闭包的函数 ** 返回值 必须是一个 布尔值,** 其次,这个闭包的参数 在find函数里面自动帮我们传递了, 我们可以看出来他的参数的值 其实就是这个调用者字符串的 每一个字符。
groovy 闭包进阶--闭包的委托策略
首先 我们要搞清楚 this owner 和 delegate 的 含义
从上图可以看出, 这三个东西 都是一个值。 那groovy 为啥要搞三个东西?
this:代表闭包定义处的类 这个很好理 我们根据上图可以知道 我们定义这个c 闭包 所处的类 就是ClouTest类 owner:代表闭包定义处的类 或者 闭包定义处的对象 delegate 代表任意对象
多数情况下,这三者的作用 就是一致的。 但是某些场景下, 这三者并不一样。 我们继续来看几个例子
我们在 ClosureTest 里面定义一个内部类
class Person {
def static c1 = {
println(this)
println(owner)
println(delegate)
}
def static s() {
def c2 = {
println(this)
println(owner)
println(delegate)
}
c2()
}
}
然后看一下 执行结果
你会发现 他指向的就是这个内部类了,不是内部类的对象喔, 因为上面都是static 方法。
我们去掉static 关键字以后 再看一次执行结果,这里所指向的 就是具体的对象了。
接着我们定义 一个闭包中的闭包 并看看他的执行结果
执行结果可以看出一个结论: 闭包中定义一个闭包的时候 owner和delegate 都会指向 最近的闭包。而this永远指向 闭包所处的类
那deleagate 什么时候与owner 不一致呢? 我们下面看一个例子就能明白了
class Student {
String name
Student(String name) {
this.name = name
}
def pretty = { "my name is ${name}" }
@Override
String toString() {
pretty()
}
}
class Teacher {
Teacher(String name) {
this.name = name
}
String name
}
def stu = new Student("wuyue")
def tea = new Teacher("android")
println(stu.toString())
这段代码 大家相信都能看的出来执行结果 是
那如果 此时我们希望 stu 的输出结果 是 my name is android 怎么办?
其实只要稍微修改一下即可:
我们手动将stu的pretty的闭包 设置一下代理对象为 tea ,然后设置闭包的策略为 代理优先即可。
这个delegate的闭包用法 在很多gradle plugin的开源代码中都能碰到,所以这里单独拎出来讲一下。其实本质上就是个代理模式, 和kotlin 中的by 关键字 有一些类似。有兴趣的同学 可以再自己多写几个例子好好体会一下。