Android如今的構建工具用的是gradle,很早之前就有過接觸,只不過歷來沒有用到實際的項目中。java
這兩天在看gradle的一些官方文檔和官方推薦的書,並結合着項目的build腳本。對gradle及其所用的groovy語言大體有了個認識。不過時間一直有一個問題在困擾我:android
buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } }
如上述代碼所示 buildTypes 的是一個method,那麼其後的block應該是一個closure,可是buildTypes的方法聲明倒是這樣的api
/** * Configures the build types. */ void buildTypes(Action> action) { checkWritability() action.execute(buildTypes) }
這裏,我就納悶了,明明接受的是一個Action 類型的參數啊,爲啥build腳本里面給的是一個Closure呢?我第一反應,確定是哪裏把Closure強轉成了Action(事實證實,第一反應得方向是對的,哈哈)。函數
不過我找了N久,就是找不到哪裏調用了buildTypes,並在這以前把參數類型作了強轉。尼瑪,糾結了很久。後來我就想,既然在代碼裏面找不到直接的轉換的代碼,是否是多是在gradle內部作了呢?工具
順着這個思路,我本身寫了個gradle腳本,很簡單gradle
def buildTypes(Action action) { println '' def text = "action is called here" action.execute(text) } interface Command { void balabala(String test); } def testCommand(Command test) { println '' test.balabala("balabala......") } task hello << { println 'hello task is called' buildTypes { println 'Action for buildTypes' } testCommand { String para -> println 'Command->' + para } }
上面這段代碼的運行結果以下:ui
能夠看到task hello中buildTypes和testCommand方法後的closure都已經被調用了,並且經過Command接口中的balabala方法傳遞的參數,testCommand後的closure也能收到。this
那麼能夠肯定的是,Closure必定在某個時候被強轉成了Action(其實對了一半)。另外能夠看到不單單是gradle本身的Action接口能夠這樣,本身寫得Command接口也是能夠的,因此url
幹這個事情的很大程度上應該不是gradle,那麼剩下的應該就是groovy啦。code
因而我又作了一個實驗,直接寫一個groovy的腳原本驗證一下
interface Command { void excute(String a, Integer b) } def buildTypes(Command cmd) { println '++++++++buildTypes was called' cmd.excute('meituan', 2015) } buildTypes { String a, Integer b -> println 'fairysword ' + a + '###' + b } buildTypes { String a, Integer b -> println 'uabearbest ' + a + '@@@' + b }
上面這段腳本運行結果以下
嗯,到這裏,應該能夠看出來,的確是groovy乾的,不過怎麼幹的,咱仍是不知道。不過咱們知道groovy和java都是運行在JVM上,groovy也是編譯成java字節碼的,
因此,我試着把上述腳本直接編譯後,研究一下。先把腳本編譯成class文件,在反編譯成java文件。結果以下
/* * Decompiled with CFR 0_102. * * Could not load the following classes: * Command * groovy.lang.Binding * groovy.lang.GroovyObject * groovy.lang.MetaClass * groovy.lang.Script * org.codehaus.groovy.reflection.ClassInfo * org.codehaus.groovy.runtime.InvokerHelper * org.codehaus.groovy.runtime.ScriptBytecodeAdapter * org.codehaus.groovy.runtime.callsite.CallSite * org.codehaus.groovy.runtime.callsite.CallSiteArray * test$_run_closure1 * test$_run_closure2 */ import Command; import groovy.lang.Binding; import groovy.lang.GroovyObject; import groovy.lang.MetaClass; import groovy.lang.Script; import org.codehaus.groovy.reflection.ClassInfo; import org.codehaus.groovy.runtime.InvokerHelper; import org.codehaus.groovy.runtime.ScriptBytecodeAdapter; import org.codehaus.groovy.runtime.callsite.CallSite; import org.codehaus.groovy.runtime.callsite.CallSiteArray; import test; public class test extends Script { private static /* synthetic */ ClassInfo $staticClassInfo; public static transient /* synthetic */ boolean __$stMC; private static /* synthetic */ SoftReference $callSiteArray; public test() { test test; CallSite[] arrcallSite = test.$getCallSiteArray(); } public test(Binding context) { CallSite[] arrcallSite = test.$getCallSiteArray(); super(context); } public static /* varargs */ void main(String... args) { CallSite[] arrcallSite = test.$getCallSiteArray(); arrcallSite[0].call((Object) InvokerHelper.class, (Object) test.class, (Object) args); } public Object run() { CallSite[] arrcallSite = test.$getCallSiteArray(); arrcallSite[1].callCurrent((GroovyObject) this, (Object) new _run_closure1((Object) this, (Object) this)); return arrcallSite[2].callCurrent((GroovyObject) this, (Object) new _run_closure2((Object) this, (Object) this)); } public Object buildTypes(Command cmd) { CallSite[] arrcallSite = test.$getCallSiteArray(); arrcallSite[3].callCurrent((GroovyObject) this, (Object) "++++++++buildTypes was called"); return arrcallSite[4].call((Object) cmd, (Object) "xiong", (Object) 1987); } protected /* synthetic */ MetaClass $getStaticMetaClass() { if (this.getClass() != test.class) { return ScriptBytecodeAdapter.initMetaClass((Object) this); } ClassInfo classInfo = $staticClassInfo; if (classInfo == null) { $staticClassInfo = classInfo = ClassInfo.getClassInfo(this.getClass()); } return classInfo.getMetaClass(); } private static /* synthetic */ void $createCallSiteArray_1(String[] arrstring) { arrstring[0] = "runScript"; arrstring[1] = "buildTypes"; arrstring[2] = "buildTypes"; arrstring[3] = "println"; arrstring[4] = "excute"; } private static /* synthetic */ CallSiteArray $createCallSiteArray() { String[] arrstring = new String[5]; test.$createCallSiteArray_1(arrstring); return new CallSiteArray((Class) test.class, arrstring); } private static /* synthetic */ CallSite[] $getCallSiteArray() { CallSiteArray callSiteArray; if ($callSiteArray == null || (callSiteArray = (CallSiteArray) $callSiteArray.get()) == null) { callSiteArray = test.$createCallSiteArray(); $callSiteArray = new SoftReference<CallSiteArray>(callSiteArray); } return callSiteArray.array; } }
研究這段代碼,能夠看出所用的調用點(即groovy所謂的CallSite)都被groovy統一作了處理,在這個groovy的腳本中總共存在5處調用
private static /* synthetic */ void $createCallSiteArray_1(String[] arrstring) { arrstring[0] = "runScript"; arrstring[1] = "buildTypes"; arrstring[2] = "buildTypes"; arrstring[3] = "println"; arrstring[4] = "excute"; }
對buildTypes的兩處調用體如今run函數中
public Object run() { CallSite[] arrcallSite = test.$getCallSiteArray(); arrcallSite[1].callCurrent((GroovyObject) this, (Object) new _run_closure1((Object) this, (Object) this)); return arrcallSite[2].callCurrent((GroovyObject) this, (Object) new _run_closure2((Object) this, (Object) this)); }
至此,咱們終於能夠看到groovy並無把Closure轉成Action,而是無差異的都轉成了Object,這也解釋了爲啥buildTypes方法接受的是Command類型的參數,可是實際上你傳給他一個Closure參數,也能正常work