• 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

  • 相关阅读:
    uboot的启动过程-FDT
    pat练习
    也不知道 为什么没通过 -------------存疑 ---延后解决
    刚认识--博客园
    MapReduce报错Exception in thread "main" java.lang.UnsatisfiedLinkError: org.apache.hadoop.io.nativeio
    Hadoop 学习笔记(十)HDFS 之 集群安全模式 & DateaNode
    Hadoop 学习笔记(九)HDFS 之 NameNode & SecondaryNameNode
    sqlldr 报错:Field in data file exceeds maximum length 解决方法
    Hadoop 学习笔记(八)HDFS 数据的读写
    Hadoop 学习笔记(七)HDFS 客户端操作(下)
  • 原文地址:https://www.cnblogs.com/lotusJade/p/4881950.html
Copyright © 2020-2023  润新知