很多程序员或者老师都说 build.gradle中写的是groovy代码,
但下边代码有的看起来有些特殊,不知道各位有没有思考过
比如:
productFlavors {
xiaomi {
}
oppo {
}
}
根据对groovy的学习,我们知道productFlavors是一个方法调用,后边的花括号是个闭包,这个闭包是方法的参数,从AGP的源码中我们也能找到这个方法,里边的xiaomi 、oppo看起来又像两个方法调用,分别传了一个闭包参数,但是用过productFlavors的朋友想必都知道,像xiaomi oppo这些单词都可以在里边随意写,写个xiaomi1 xiaomi2 zhangsan lisi 都可以,可是这些不是方法调用吗,方法不是在编译时就已经确定的吗,怎么做到的可以随便写的,其实秘密就在NamedDomainObjectContainer。
我们一步一步看,productFlavors 是BaseExtensions的方法:
/**
* Encapsulates all product flavors configurations for this project.
*
* <p>For more information about the properties you can configure in this block, see {@link
* ProductFlavor}
*/
public void productFlavors(Action<? super NamedDomainObjectContainer<ProductFlavor>> action) {
checkWritability();
action.execute(productFlavors);
}
这个方法接收一个Action类型的参数,之所以在build.gradle中调用的时候可以用闭包的形式传递,是因为最终加载的类根本不是AppExtension(继承自BaseExtension),而是AppExtension_Decorated。在gradle中几乎所有非gradle自身类的加载都是通过org.gradle.internal.instantiation.generator包中的加载机制实现的,其会根据目标类的一些注解等其他参数在目标类的字节码的基础上通过asm框架生成特定的一套字节码,并加载成class,这套字节码就是带 _Decorated或 _Injected后缀的类,这些类会增加一些gradle自己需要用到的相关处理,比如原类中有一个参数为 Action 的方法,生成的类中会添加一个 参数为闭包的对应方法,具体做法是AbstractClassGenerator.DslMixInPropertyType.addMissingClosureOverloads():
private void addMissingClosureOverloads(ClassGenerationVisitor visitor) {
for (Method method : actionMethods) {
Method overload = findClosureOverload(method, closureMethods.get(method.getName()));
if (overload == null) {
visitor.addActionMethod(method);
}
}
}
addActionMethod:
@Override
public void addActionMethod(Method method) {
if (!mixInDsl) {
return;
}
Type returnType = getType(method.getReturnType());
@SuppressWarnings("NullableProblems")
Type[] originalParameterTypes = collectArray(method.getParameterTypes(), Type.class, Type::getType);
int numParams = originalParameterTypes.length;
Type[] closurisedParameterTypes = new Type[numParams];
System.arraycopy(originalParameterTypes, 0, closurisedParameterTypes, 0, numParams);
closurisedParameterTypes[numParams - 1] = CLOSURE_TYPE;
final String methodDescriptor = getMethodDescriptor(returnType, closurisedParameterTypes);
// GENERATE public <return type> <method>(Closure v) { return <method>(…, ConfigureUtil.configureUsing(v)); }
publicMethod(method.getName(), methodDescriptor, methodVisitor -> new MethodVisitorScope(methodVisitor) {{
// GENERATE <method>(…, ConfigureUtil.configureUsing(v));
_ALOAD(0);
int stackVar = 1;
for (int typeVar = 0; typeVar < numParams - 1; ++typeVar) {
Type argType = closurisedParameterTypes[typeVar];
_ILOAD_OF(argType, stackVar);
stackVar += argType.getSize();
}
// GENERATE ConfigureUtil.configureUsing(v);
_ALOAD(stackVar);
_INVOKESTATIC(CONFIGURE_UTIL_TYPE, "configureUsing", getMethodDescriptor(ACTION_TYPE, CLOSURE_TYPE));
_INVOKEVIRTUAL(generatedType, method.getName(), getMethodDescriptor(getType(method.getReturnType()), originalParameterTypes));
_IRETURN_OF(returnType);
}});
}
注释也写明了,// GENERATE public (Closure v) { return (…, ConfigureUtil.configureUsing(v)); }
productFlavors对应的闭包参数方法就应该是
public void productFlavors(Closure configureClosure) {
productFlavors(ConfigureUtil.configureUsing(v));
}
以上就是为什么productFlavors可以传闭包,
继续看configureUsing方法, 返回了一个实现了Action接口的包装类。
/**
* Creates an action that uses the given closure to configure objects of type T.
*/
public static <T> Action<T> configureUsing(@Nullable final Closure configureClosure) {
if (configureClosure == null) {
return Actions.doNothing();
}
return new WrappedConfigureAction<T>(configureClosure);
}
很显然,productFlavors方法中action.execute(productFlavors)的action就是这个WrappedConfigureAction了,也就是说接下来调用了WrappedConfigureAction的execute方法,该方法又执行到了ConfigureUtil的静态方法中
public static <T> T configure(@Nullable Closure configureClosure, T target) {
if (configureClosure == null) {
return target;
}
if (target instanceof Configurable) {
((Configurable) target).configure(configureClosure);
} else {
configureTarget(configureClosure, target, new ConfigureDelegate(configureClosure, target));
}
return target;
}
target是NamedDomainObjectContainer接口的实现类对象,这里我就直接说出来这个对象的实际类型是FactoryNamedDomainObjectContainer且泛型为ProductFlavor,这个类实际上是实现了Configurable接口的,也就是执行的
((Configurable) target).configure(configureClosure); 而参数 configureClosure就是我们传入的那个闭包,即
{
xiaomi {
}
oppo {
}
}
接下来调用AbstractNamedDomainObjectContainer的configure方法,
@Override
public AbstractNamedDomainObjectContainer<T> configure(Closure configureClosure) {
ConfigureDelegate delegate = createConfigureDelegate(configureClosure);
ConfigureUtil.configureSelf(configureClosure, this, delegate);
return this;
}
delegate的类型是NamedDomainObjectContainerConfigureDelegate。
然后到ConfigureUtil的configureSelf方法:
public static <T> T configureSelf(@Nullable Closure configureClosure, T target, ConfigureDelegate closureDelegate) {
if (configureClosure == null) {
return target;
}
configureTarget(configureClosure, target, closureDelegate);
return target;
}
参数configureClosure是原始闭包,target是FactoryNamedDomainObjectContainer实例,closureDelegate是NamedDomainObjectContainerConfigureDelegate。
继续,到configureTarget方法
private static <T> void configureTarget(Closure configureClosure, T target, ConfigureDelegate closureDelegate) {
if (!(configureClosure instanceof GeneratedClosure)) {
new ClosureBackedAction<T>(configureClosure, Closure.DELEGATE_FIRST, false).execute(target);
return;
}
// Hackery to make closure execution faster, by short-circuiting the expensive property and method lookup on Closure
Closure withNewOwner = configureClosure.rehydrate(target, closureDelegate, configureClosure.getThisObject());
new ClosureBackedAction<T>(withNewOwner, Closure.OWNER_ONLY, false).execute(target);
}
这里需要一些groovy的闭包知识,闭包就是一段代码块,这段代码可以执行,它有三个内置变量,this,delegate还有owner,这三个变量分别指向一个对象,相当于闭包的执行环境,即闭包里方法的索引会根据闭包的策略(resolveStrategy)先后从这三个变量指向的对象中查找。
至于这三个变量的设置,开发者可以根据情况自行设置,当然其也有默认值,感兴趣的朋友可以自己查一些相关资料。
configureTarget方法中,调用了原始闭包对象的rehydrate方法克隆了一个对象出来,并设置了delegate指向target,owner指向closureDelegate,this指向configureClosure.getThisObject()。然后创建了一个ClosureBackedAction对象,其中第二个参数为Closure.OWNER_ONLY,应该可以猜到这是设置的闭包方法解析策略(resolveStrategy)。
接下来到ClosureBackedAction的execute方法的关键代码:
if (configurableAware && delegate instanceof Configurable) {
((Configurable) delegate).configure(closure);
} else {
Closure copy = (Closure) closure.clone();
copy.setResolveStrategy(resolveStrategy);
copy.setDelegate(delegate);
if (copy.getMaximumNumberOfParameters() == 0) {
copy.call();
} else {
copy.call(delegate);
}
}
configurableAware为false,执行else,resolveStrategy为OWNER_ONLY,而owner指向的是NamedDomainObjectContainerConfigureDelegate对象,也就是说闭包
{
xiaomi {
}
oppo {
}
}
执行的时候要查找方法,会到NamedDomainObjectContainerConfigureDelegate对象中查找。
我们知道gradle脚本是用groovy语言写的,groovy语言中方法的调用都不是直接用方法索引调用的,这也是其能实现动态的原理,也就是说你在写代码时随便写一个方法调用,编译器是不会报错的,运行时jvm会根据你提供的索引去对应的执行环境中查找方法的具体位置,如果没有找到的话就会执行receiver的invokeMethod方法,默认的该方法应该是抛出一个异常。回到我们的问题上来,闭包中执行到xiaomi,识别到这是一个方法调用,很显然,执行环境或者叫receiver(NamedDomainObjectContainerConfigureDelegate)中不可能有这个方法的定义,那么就会执行NamedDomainObjectContainerConfigureDelegate的invokeMethod方法,该方法由父类ConfigureDelegate实现,
@Override
public Object invokeMethod(String name, Object paramsObj) {
Object[] params = (Object[])paramsObj;
boolean isAlreadyConfiguring = _configuring;
_configuring = true;
try {
DynamicInvokeResult result = _delegate.tryInvokeMethod(name, params);
if (result.isFound()) {
return result.getValue();
}
if (!isAlreadyConfiguring) {
// Try to configure element
result = _configure(name, params);
if (result.isFound()) {
return result.getValue();
}
}
// try the owner
result = _owner.tryInvokeMethod(name, params);
if (result.isFound()) {
return result.getValue();
}
throw _delegate.methodMissingException(name, params);
} finally {
_configuring = isAlreadyConfiguring;
}
}
关键代码
if (!isAlreadyConfiguring) {
// Try to configure element
result = _configure(name, params);
if (result.isFound()) {
return result.getValue();
}
}
接下来_configure(name, params)方法调用,
NamedDomainObjectContainerConfigureDelegate重写了该方法:
@Override
protected DynamicInvokeResult _configure(String name, Object[] params) {
if (params.length == 1 && params[0] instanceof Closure) {
return DynamicInvokeResult.found(_container.create(name, (Closure) params[0]));
}
return DynamicInvokeResult.notFound();
}
关键代码: _container.create(name, (Closure) params[0])
_container类型就是FactoryNamedDomainObjectContainer,该类其实是一个具有创建实例功能的容器。
接下来就是
FactoryNamedDomainObjectContainer中创建了一个指定的泛型类型的对象,即 ProductFlavor对象,并且存放到一个类似map中的容器(这个容器以后有机会再讲)中,key为"xiaomi", xiaomi后边跟着一个闭包,接下来就是用该闭包配置刚才创建的对象,大致就是把该对象配置为闭包的执行环境,然后执行闭包。直观的感受就是,
你可以在xiaomi后边的花括号里调用ProductFlavor中的方法或者修改其成员变量的值。
例:
productFlavors {
xiaomi {
setApplicationId("com.example.demooooo")
}
}
意思就是创建一个 名字为xiaomi的ProductFlavor对象放到了容器中,并调用了该对象setApplicationId设置了id。