Android 开发之Gradle《三》DynamicObject

前言

在前一篇文章里面介绍了build.gradle 里面方法调用的路由过程,知道了build.gradle 内部调用方法会通过DynamicObject 映射到对应的Project类里面。

图一

build.gradle 脚本在编译之后会生成一个BasicScript子类,BasicScript有一个成员变量target,放到build.gradle脚本里面来讲target 就是Project 对象,setting.gradle 脚本里面target就是Settings对象。假如build.gradle 脚本里面调用了一个不是定义在脚本里面的方法,那么Gradle会通过target查找这个方法。具体路由过程参见上一篇文章。对于build.gradle 脚本而言,因为target 为Project,因此先调用功arget的getAsDynamicObject()方法,最终调用getAsDynamicObject 返回值的tryInvokeMethod()路由最终被调用的方法。

一、DynamicObject

public interface DynamicObject extends MethodAccess, PropertyAccess {
 
    MissingPropertyException getMissingProperty(String name);

    MissingPropertyException setMissingProperty(String name);

    MissingMethodException methodMissingException(String name, Object... params);

    Object getProperty(String name) throws MissingPropertyException;

    void setProperty(String name, Object value) throws MissingPropertyException;

    Object invokeMethod(String name, Object... arguments) throws MissingMethodException;
}

依照我的理解DynamicObject 更像是一个方法,属性的路由器,它本身有点代理的意思,当我们调用某个方法的时候,他会在被代理的一个或者是多个对象里面查找对应的方法是否存在,存在的话就执行这个方法。
接口的一个抽象实现是 AbstractDynamicObject,它继承自DynamicObject,是 Gradle 脚本函数调用的关键。

public abstract class AbstractDynamicObject implements DynamicObject {
	xx
    @Override
    public Object invokeMethod(String name, Object... arguments) throws groovy.lang.MissingMethodException {
    	//调用tryInvokeMethod
        DynamicInvokeResult result = tryInvokeMethod(name, arguments);
        if (result.isFound()) {
            return result.getValue();
        }
        throw methodMissingException(name, arguments);
    }
}

AbstractDynamicObject 的子类需要重写tryInvokeMethod 方法实现方法的路由。
这里以AbstractDynamicObject的子类BeanDynamicObject为例。

    @Override
    public DynamicInvokeResult tryInvokeMethod(String name, Object... arguments) {
    	MetaClass metaClass;
       if (bean instanceof GroovyObject) {
           metaClass= ((GroovyObject) bean).getMetaClass();
        } else {
            metaClass= GroovySystem.getMetaClassRegistry().getMetaClass(bean.getClass());
        }
        //通过MetaClass 获取对应的方法
     MetaMethod metaMethod =   metaClass.pickMethod(name, arguments);
      if (metaMethod != null) {
      			//调用对应的方法
              return DynamicInvokeResult.found(metaMethod.doMethodInvoke(bean, arguments));
       }

        if (bean instanceof MethodMixIn) {
                MethodMixIn methodMixIn = (MethodMixIn) bean;
                return methodMixIn.getAdditionalMethods().tryInvokeMethod(name, arguments);
            }
      return DynamicInvokeResult.notFound();;
    }

这里的的tryInvokeMethod 是简化之后的代码,实际实现比这复杂一些,但是总体逻辑是没变的。bean 是可以看成的是被代理的对象,它是方法的实现者。

MetaClass 是Groovy 里面的一个类,他里面包含着一个对象的所有方法与属性,有不了解的同学可以查阅一下相关的资料。通过metaClass.pickMethod(name, arguments) 方法查询bean里面是不是存在相应的方法,如果存在就执行这个方法。如果不存在,那么判断bean 是不是实现了MethodMixIn 接口,若是就调用methodMixIn.getAdditionalMethods().tryInvokeMethod(name, arguments); 方法。这一点在后面会用到,因此在这里提前说一下。

二、ExtensibleDynamicObject

前面说了当在build.gradle被编译为一个BasicScript 的子类,这个子类的target 是Project。当在build.gradle 里面调用一个脚本不存在的方法的时候会调用target 的getAsDynamicObject 方法返回值的tryInvokeMethod 方法。
Project 的默认实现是DefaultProject。

DefaultProject.java

  private ExtensibleDynamicObject extensibleDynamicObject;
   @Override
    public DynamicObject getAsDynamicObject() {
        return extensibleDynamicObject;
    }

ExtensibleDynamicObject 是DynamicObject 的一个子类。ExtensibleDynamicObject 可以看成是多个对象的代理,它会在所有的被代理的对象里面查找对应的方法。Project 给我们提供了动态添加被代理的对象的API。

2.1 Extension

Gradle 的 Extension,翻译成中文意思就叫扩展。它的作用就是通过实现自定义的 Extension,可以在 Gradle 脚本中增加类似 命名空间的配置,Gradle 可以识别这种配置,并读取里面的配置内容。

图二

上面就是添加了一个名为testCon 的TestConvention 扩展。换句话说就是相当于添加了下面这样一个方法

TestConvention testCon(){    
}

之后我们就可以通过扩展的名字获取到这个扩展并配置这个扩展,
看到这里大家是不是对下面这段代码有了新的认识。
图三
android 插件添加的一个名为android 类型为AppExtension 的扩展。
下面我们看看实现原理。

    @Override
    public ExtensionContainerInternal getExtensions() {
        return (ExtensionContainerInternal) getConvention();
    }
    @Override
    public Convention getConvention() {
        return extensibleDynamicObject.getConvention();
    }

extensibleDynamicObject 就是ExtensibleDynamicObject。

2.2 构造

    public ExtensibleDynamicObject(Object delegate, Class<?> publicType, Instantiator instantiator) {
     //delegate 表示一个被代理的类,这里可以看成是DefaultProject
        this(delegate, createDynamicObject(delegate, publicType), new DefaultConvention(instantiator));
    }

    public ExtensibleDynamicObject(Object delegate, AbstractDynamicObject dynamicDelegate, Instantiator instantiator) {
        this(delegate, dynamicDelegate, new DefaultConvention(instantiator));
    }

    public ExtensibleDynamicObject(Object delegate, AbstractDynamicObject dynamicDelegate, Convention convention) {
        this.dynamicDelegate = dynamicDelegate;
        this.convention = convention;
        this.extraPropertiesDynamicObject = new ExtraPropertiesDynamicObjectAdapter(delegate.getClass(), convention.getExtraProperties());

        updateDelegates();
    }

    private static BeanDynamicObject createDynamicObject(Object delegate, Class<?> publicType) {
     //BeanDynamicObject 在前面介绍了
        return new BeanDynamicObject(delegate, publicType);
    }

    private void updateDelegates() {
        DynamicObject[] delegates = new DynamicObject[6];
        //表示Project
        delegates[0] = dynamicDelegate;
        //ext
        delegates[1] = extraPropertiesDynamicObject;
        int idx = 2;
        //
        if (beforeConvention != null) {
            delegates[idx++] = beforeConvention;
        }
        if (convention != null) {
            delegates[idx++] = convention.getExtensionsAsDynamicObject();
        }
        if (afterConvention != null) {
            delegates[idx++] = afterConvention;
        }
        boolean addedParent = false;
        if (parent != null) {
            addedParent = true;
            //可以看成函是父Project
            delegates[idx++] = parent;
        }
        DynamicObject[] objects = new DynamicObject[idx];
        System.arraycopy(delegates, 0, objects, 0, idx);
        setObjects(objects);

        if (addedParent) {
            idx--;
        }
        objects = new DynamicObject[idx];
        System.arraycopy(delegates, 0, objects, 0, idx);
        setObjectsForUpdate(objects);
    }

DynamicObject[] delegates = new DynamicObject[6]; 意思是指 ExtensibleDynamicObject 有6个方法的 Delegate,查找方法的调用最终会按顺序在这6个 Delegate 中去寻找。

我们这里以Project里面的ExtensibleDynamicObject 为例介绍这六个Delegate。

1 delegates[0] 是个BeanDynamicObject,用于在Projec里面查找对应的方法或属性

2 delegates[1] 是一个DynamicObject,用于在ext 里面路由响应的方法或属性

图五
ext 是ExtensionsStorage类型的一个对象,可以看成是一个Map。

3 delegates[2] 也是一个DynamicObject,用于在脚本里面查找对应的方法或属性。

4 delegates[3] 也是一个DynamicObjec,用于在用户里面查找对应的方法或属性。

project.getExtensions().add("testCon",new TestConvention())

图六

5 delegates[4] 也是一个DynamicObjec,由于查找与Task有关系的方法或属性,这里不关心这个。

6 delegates[5] 也是一个DynamicObjec,用于在父 ExtensibleDynamicObject查找,例如在父project的build.gradle 脚本里面定义的方法都可以在子Project的build.gradle 里面调用。
图四

    public DynamicInvokeResult tryInvokeMethod(String name, Object... arguments) {
     	
     	DynamicInvokeResult result;
     	//objects 就是上面的DynamicObject 数组
        for (DynamicObject object : objects) {
            DynamicInvokeResult result = object.tryInvokeMethod(name, arguments);
            if (result.isFound()) {
                return result;
            }
        }
       if (result.isFound()) {
            return result;
        }
    	//如果没名为name的方法,那么尝试获取名为name的属性
        DynamicInvokeResult propertyResult = tryGetProperty(name);
        if (propertyResult.isFound()) {
            Object property = propertyResult.getValue();
            if (property instanceof Closure) {
             //如果是Closure 属性,那么就用相应的参数执行这个Closure
                Closure closure = (Closure) property;
                closure.setResolveStrategy(Closure.DELEGATE_FIRST);
                BeanDynamicObject dynamicObject = new BeanDynamicObject(closure);
                result = dynamicObject.tryInvokeMethod("doCall", arguments);
                if (!result.isFound() && !(closure instanceof GeneratedClosure)) {
                    return DynamicInvokeResult.found(closure.call(arguments));
                }
                return result;
            }
        }
        return DynamicInvokeResult.notFound();
    }

当调用方法的时候首先会在遍历DynamicObject 数组,查询是否存在对应的方法,如果存在那么就执行该方法,如果不存在就会在DynamicObject 数组里面查询是否存在与方法名称一样并且类型为Closure 的属性,如果存在将Closure 看成是一个方法并且执行这个Closure 。

总结

到此我们就介绍完了在脚本里面调用一个方法的路由过程,下一篇介绍依赖。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值