Gradle源码解析(一)productFlavors是如何创建多渠道的

很多程序员或者老师都说 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。


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
在 Android 开发中,Gradle 是一个非常重要的构建工具,可以用来构建和打包 Android 应用程序。Gradle 插件是一种工具,可以扩展 Gradle 的功能,使其能够支持更多的功能。而多渠道打包是 Android 应用程序开发中非常重要的一个方面,它可以让我们将应用程序打包成不同的版本,并发布到不同的应用商店或市场上。 在 Android Studio 中,我们可以通过自定义 Gradle 插件来实现多渠道打包,具体步骤如下: 1. 创建 Gradle 插件项目 在 Android Studio 中创建一个新项目,选择 Gradle 插件项目模板。这将创建一个 Gradle 插件项目,并生成一些默认的代码和文件。 2. 实现多渠道打包 在插件项目中,我们需要实现多渠道打包的功能。这可以通过 GradleproductFlavors 和 buildTypes 配置来实现。我们可以定义多个 productFlavors,并为每个 productFlavor 配置不同的参数,例如应用程序的包名、应用程序名称等。在 buildTypes 中,我们可以为每个 buildType 配置不同的参数,例如应用程序的版本号、是否开启混淆等。 3. 打包应用程序 在插件项目中,我们可以编写一个 Gradle 任务来实现应用程序的打包。这个任务可以使用 Gradle 提供的 assemble 任务来实现。我们可以为每个 productFlavor 和 buildType 配置不同的打包参数,并使用 Gradle 的 assemble 任务来生成应用程序的 APK 文件。 4. 发布应用程序 在插件项目中,我们可以编写一个 Gradle 任务来实现应用程序的发布。这个任务可以使用 Gradle 提供的 uploadArchives 任务来实现。我们可以为每个 productFlavor 和 buildType 配置不同的发布参数,并使用 Gradle 的 uploadArchives 任务将应用程序发布到不同的应用商店或市场上。 总的来说,自定义 Gradle 插件多渠道打包是 Android 应用程序开发中非常重要的一个方面。通过自定义 Gradle 插件,我们可以实现更加灵活和高效的应用程序打包和发布。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值