zoukankan      html  css  js  c++  java
  • Groovy Closure & Action

    Android现在的构建工具用的是gradle,很早以前就有过接触,只不过从来没有用到实际的项目中。

    这两天在看gradle的一些官方文档和官方推荐的书,并结合着项目的build脚本。对gradle及其所用的groovy语言大致有了个认识。不过期间一直有一个问题在困扰我:

        buildTypes {
            release {
                minifyEnabled false
                proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            }
        }

    如上述代码所示 buildTypes 的是一个method,那么其后的block应该是一个closure,但是buildTypes的方法声明却是这样的

        /**
         * Configures the build types.
         */
        void buildTypes(Action> action) {
            checkWritability()
            action.execute(buildTypes)
        }

    这里,我就纳闷了,明明接受的是一个Action 类型的参数啊,为啥build脚本里面给的是一个Closure呢?我第一反应,肯定是哪里把Closure强转成了Action(事实证明,第一反应得方向是对的,哈哈)。

    不过我找了N久,就是找不到哪里调用了buildTypes,并在这之前把参数类型做了强转。尼玛,纠结了好久。后来我就想,既然在代码里面找不到直接的转换的代码,是不是可能是在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
            }
        }

           上面这段代码的运行结果如下:

           

           可以看到task hello中buildTypes和testCommand方法后的closure都已经被调用了,而且通过Command接口中的balabala方法传递的参数,testCommand后的closure也能收到。

    那么可以确定的是,Closure一定在某个时候被强转成了Action(其实对了一半)。另外可以看到不仅仅是gradle自己的Action接口可以这样,自己写得Command接口也是可以的,所以

    干这个事情的很大程度上应该不是gradle,那么剩下的应该就是groovy啦。

         于是我又做了一个实验,直接写一个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

  • 相关阅读:
    简单的模板解析函数
    HTML通过事件传递参数到js 二 event
    HTML通过事件传递参数到js一
    通过this获取当前点击选项相关数据
    LeetCode 20. 有效的括号(Valid Parentheses)
    LeetCode 459. 重复的子字符串(Repeated Substring Pattern)
    LeetCode 14. 最长公共前缀(Longest Common Prefix)
    LeetCode 168. Excel表列名称(Excel Sheet Column Title)
    LeetCode 171. Excel表列序号(Excel Sheet Column Number) 22
    LeetCode 665. 非递减数列(Non-decreasing Array)
  • 原文地址:https://www.cnblogs.com/lotusJade/p/4881950.html
Copyright © 2011-2022 走看看