我正在Gradle(Android Studio)中构建Android应用程序的不同产品风格。
因此,我定义了以下产品口味:
android {
project.ext.set("customer", "")
project.ext.set("server", "")
//Configuration happens here - code removed for readability
buildTypes {
debug {
server = "test"
}
release {
server = "release"
}
}
//Available product flavors
productFlavors {
customerA{
customer = "a"
}
customerB{
customer = "b"
}
customerC{
customer = "c"
}
}
}
但是,稍后,当我在一个构建任务中访问定义的项目属性“客户”(其值在我当前正在构建的产品风格中设置)时,它总是具有值“c”,即使iam构建客户A(在这种情况下,属性客户应该是“a”而不是“c”)。例如,我稍后会执行以下任务:
preBuild << {
println "Building customer: " + customer
}
它总是打印:
建筑客户:c
所以我猜有一些覆盖发生?可能与配置VS执行阶段有关?虽然不知道如何/为什么,所以非常感谢任何帮助。
更新:或者,它已经让我在gradle构建的执行阶段进一步确定产品风格的名称(没有附加构建类型名称)和构建类型(再次:没有附加产品风格名称)。
考虑到上述配置,预期的产品风格名称将是:客户A、客户B和客户C。
在评估阶段,Gradle执行你的android
块中的所有代码;它不仅仅执行与你想要编译的风格相关的代码。事实上,在评估阶段,它甚至不知道你的口味是什么;它必须评估才能找到答案。
因此,您的所有三行客户="a"
、客户="b"
和客户="c"
都将被执行。
这是关于Gradle的微妙之处之一,这使得它有点难以学习。
所以我已经解释了为什么你的代码没有按照你期望的方式工作,但是这个答案是不完整的,因为我没有说很多关于如何做才能让它正常工作的事情,但是很难说要做什么,因为我不确定你想完成什么。总的来说,我可以说你应该考虑使用用户定义的任务来完成你想要的事情,并设置任务内依赖关系以确保事情以正确的顺序执行。Android Gradle构建的一个问题是,即使这些任务在评估阶段才被定义(它不知道它需要什么任务来构建所有的风格,直到它评估了构建文件并知道这些风格是什么),所以做一些SO的调查来看看如何将事情挂钩到Android Gradle构建任务上-你必须在Android插件完成任务后的评估阶段结束时设置你的任务。
非常感谢斯科特·巴塔,感谢他的建议和解释,为什么我的解决方案不起作用(这也让我重新考虑了一些事情)。我基本上想出了不同的方法来完成我需要的事情。
除非你需要做的事情不能通过简单地根据构建类型和风格(即通过约定)组织你的Android资源树来实现,否则我推荐选项2。尽管我确实保留了选项1作为参考,因为它涵盖了productFlavor属性扩展的有趣主题。
我将提供一个非常简单且类似的示例,如上所述,尽管在我的例子中我需要一个String属性,而不是布尔值。
// This class will be used to create our custom property
class StringExtension {
String value
StringExtension (String value) {
this.value = value
}
public void setValue(String value) {
this.value = value
}
public String getValue() {
return value
}
}
android {
// Add our new property to each product flavor upon creation
productFlavors.whenObjectAdded { flavor ->
//I am suspecting the last argument is the default value
flavor.extensions.create("myProperty", StringExtension , '')
}
// then we can set the value on the extension of any flavor object
productFlavors {
customerA{
myProperty.value 'customerA'
}
customerB{
myProperty.value 'customerB'
}
}
}
//Adds a custom action to the preBuild task
preBuild << {
//Iterate over all application variants. We name our application variant object "variant" as indicated by "variant ->"
android.applicationVariants.all { variant ->
//Here we can iterate over the flavors of our variant, well call the flavor "flavor" as indicated by "flavor ->"
variant.productFlavors.each { flavor ->
//Access our custom property "customerName"
println "Building customer" + flavor.customerName.value
}
}
}
然后我意识到,上面是完全不必要的,因为我想要的只是我的味道的名字(没有构建类型),一旦我找到了给我的味道的名字的属性,我就可以改变上面所有的代码如下:
只需通过访问名为“name”的已经存在的产品风味属性,将您的风味名称用作客户的名称。
android {
productFlavors {
customerA{
}
customerB{
}
}
}
//Adds a custom action to the preBuild task
preBuild << {
//Iterate over all application variants. We name our application variant object "variant" as indicated by "variant ->"
android.applicationVariants.all { variant ->
//Here we can iterate over the flavors of our variant, well call the flavor "flavor" as indicated by "flavor ->"
variant.productFlavors.each { flavor ->
//Access our product flavor name
println "Building customer" + flavor.name
}
}
}
上面的内容也更有意义,因为我的Android资源目录结构是以实际风格命名的。
后者也让我找到了最初问题的最终解决方案:
目的是根据每个客户的xml文件夹中的文件是发布还是调试版本来修改它。这可以通过相应的文件夹结构来实现。根据最初的问题,我们有3个客户,每个客户都有一个调试和一个发布版本。上述xml文件对于每个客户和构建类型都是不同的。因此有以下目录结构:
src/
- customerA
//Contains all relevant resource files specific to customer A
- customerB
//Contains all relevant resource files specific to customer B
- customerC
//Contains all relevant resource files specific to customer C
- customerADebug
//Contains debug server-settings file for customer A
- customerBDebug
//Contains debug server-settings file for customer B
- customerCDebug
//Contains debug server-settings file for customer C
- customerARelease
//Contains release server-settings file for customer A
- customerBRelease
//Contains release server-settings file for customer B
- customerCRelease
//Contains release server-settings file for customer C
因此,每个产品风格的主要内容都在与风格同名的文件夹中(客户A、客户B等,参见上面片段的第一部分)。现在,这个文件,根据它是每个客户的调试还是发布版本而有所不同,被放入适当的文件夹中,例如CusterADebug-
例如,当您构建客户A时,如果您构建调试或发布构建,将选择正确的文件。
回答我帖子的更新部分:
产品风味名称(不带buildType):
flavor.name(其中风味是一种产品Flavor)
以下工作为我添加自定义属性的产品口味:
android {
// ...defaultConfig...
productFlavors.whenObjectAdded { flavor ->
// Add the property 'myCustomProperty' to each product flavor and set the default value to 'customPropertyValue'
flavor.ext.set('myCustomProperty', 'customPropertyValue')
}
productFlavors {
flavor1 {
}
flavor2 {
myCustomProperty = 'alternateValue'
}
}
}
flyor1
具有自定义属性的默认值,而flyor2
具有覆盖的值。
以下是如何访问自定义属性的示例:
applicationVariants.all { variant ->
// Get the 'myCustomProperty' property from the variant's productFlavor (it's a list, but there should only be one)
def customProp = variant.productFlavors*.myCustomProperty[0]
}
我假设同样可以将自定义属性添加到构建类型,但我还没有测试过。