为什么引用不到
在 gradle 中,直接定义一个变量,再定义一个方法,在方法里里是不能引用到该变量的,但在一个闭包中就可以。
def x = 1
def fun() { println x }
fun()
android { println x }
如上图所示,fun 无法引用 x,会报错。
但 android 闭包中的 x 却能打印出来。
这是为什么呢?
由于不知道 gradle 编译后的文件在哪里,这里用 groovy 文件来测试一下,在 android studio 中运行 groovy 程序,可参见我这篇文章。
def x = 1
def fun1() { println x }
fun1()
def fun2(Closure closure) { closure() }
fun2 { println x }
运行上面的文件,会报错 Caught: groovy.lang.MissingPropertyException: No such property: x for class: MyGroovy
。
注释掉 fun1()
后,就可以正常运行。
这跟 gradle 中的现象是一样的。
由于在工具栏上的配置中运行时没有生成 build 文件夹,我们手动执行下 compileGroovy 方法,在 app/build/classes/groovy/main
查看编译后的 MyGroovy.class
。
核心代码是这样的:
public Object run() {
Reference x = new Reference(1);
this.fun1();
class _run_closure1 extends Closure implements GeneratedClosure {
}
return var1[2].callCurrent(this, new _run_closure1(this, x));
}
public Object fun1() {
CallSite[] var1 = $getCallSiteArray();
return var1[3].callCurrent(this, var1[4].callGroovyObjectGetProperty(this));
}
public Object fun2(Closure closure) {
CallSite[] var2 = $getCallSiteArray();
return var2[5].call(closure);
}
run() 方法就是 groovy 脚本执行的方法。可以看出,它先生成了一个 x 的引用,然后调用 fun1
、fun2
。
fun1
调用时,在它的内部执行了 callGroovyObjectGetProperty
方法,在获取 Groovy 对象的属性。也就是在属性中去寻找 x。
fun2
调用时,是将 x 直接传入了闭包的对象。
因为 x 并不是一个属性,只是一个临时变量,所以 fun1
获取不到,而 fun2
能获取到。
怎么引用到
既然是因为属性中没有 x,那我们把 x 加入到属性中就可以了。
看一下 gradle 中一个 project 的属性有哪些。
一个 project 有 6 种属性的作用域:
- Project 自己。
- 这个 project 的附加属性(通过 ext 定义)。
- 通过插件加入到 Project 的 extensions。
- 通过插件加入到 Project 的 convention 属性。
- 这个 project 里的 task。
- 这个 project 的父 project,递归到根 project。从这里访问到的都是只读的。
所以,将 x 的定义放在 ext
中即可:
ext { x = 1 } // 或者 ext.x = 1
def fun() { println x }
fun()
现在能正常打印出 x 的值。
另外,使用 Script 的 binding 对象也可以实现属性中添加 x。
binding.setVariable("x", 1)
def fun() { println x }
fun()