Android Studio:Gradle 产品风味:定义自定义属性
Android Studio: Gradle Product Flavors: Define custom properties
我正在 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"
}
}
}
但是,稍后,当我在我的一个构建任务中访问定义的项目 属性 "customer"(其值在我当前正在构建的产品风格中设置)时,它总是有值 "c" 即使我正在构建客户 A(在这种情况下 属性 客户应该是 "a" 而不是 "c")。例如我稍后执行以下任务:
preBuild << {
println "Building customer: " + customer
}
它总是打印:
Building customer: c
所以我猜发生了一些覆盖?可能与配置 VS 执行阶段有关?虽然不确定 how/why,因此非常感谢您的帮助。
更新:或者,它已经让我进一步确定产品风味的名称(没有附加构建类型名称)和构建类型(再次:没有在 gradle 构建的执行阶段期间添加到它前面的产品风味名称。
考虑到上述配置,预期的产品口味名称将是:customerA、customerB 和 customerC。
在评估阶段,Gradle 执行您的 android
块中的所有代码;它不只是执行与您要编译的风格相关的代码。事实上,在评估阶段,它甚至不知道你的口味是什么;它必须对其进行评估才能找出答案。
所以你的所有三行 customer = "a"
、customer = "b"
和 customer = "c"
都将被执行。
这是 Gradle 的微妙之处之一,这使得学习起来有点困难。
所以我已经解释了为什么你的代码没有按照你期望的方式工作,但是这个答案是不完整的,因为我没有说很多关于如何做才能让它正常工作,但很难说该怎么做,因为我不确定您要完成什么。一般来说,我可以说你应该考虑尝试使用用户定义的任务来完成你想要的,并设置任务内依赖关系以确保事情以正确的顺序执行。 Android Gradle 构建的一个陷阱是,即使这些任务在评估阶段之前也不会被定义(在评估构建文件并知道之前,它无法知道构建所有风格所需的任务这些风格是什么),所以做一些 SO 调查,看看如何将事情挂钩到 Android Gradle 构建任务上——你必须在 [=22] 之后的评估阶段结束时设置你的任务=] 插件已经完成了它的工作。
非常感谢 Scott Barta,感谢他的建议和解释,为什么我的解决方案不起作用(这也让我重新考虑了一些事情)。我基本上想出了不同的方法来完成我需要的。
除非你需要做的事情不能通过简单地根据构建类型和风格(即通过约定)组织你的 Android 资源树来实现,否则我会推荐选项 2。虽然我确实保留了选项 1 仅供参考,因为它涵盖了 productFlavor 属性 扩展的有趣主题。
- 基于自定义 属性 的选项:Product Flavors 允许您定义自定义属性,从而扩展 productFlavor。 Xavier Ducrohet 在这里提供了一个示例:
我将提供一个与上面提供的非常简单且相似的示例,但在我的例子中我需要一个字符串 属性,而不是布尔值。
// 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 等。请参阅上面代码段的第一部分)。现在,根据每个客户是调试版本还是发布版本,将这个文件放入适当的文件夹中,例如 customerADebug --> 包含带有调试模式等服务器设置的文件。
例如,当您构建 customerA 时,如果您构建调试或发布版本,则会选择正确的文件。
回答我 post 的更新部分:
产品风格名称(不含 buildType):
flavor.name (where flavor is a productFlavor)
以下方法对我有用,可以将自定义属性添加到产品口味中:
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'
}
}
}
flavor1
具有自定义 属性 的默认值,而 flavor2
具有覆盖值。
这是一个如何访问自定义 属性:
的示例
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]
}
我假设同样可以将自定义属性添加到构建类型,但我还没有对此进行测试。
我正在 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"
}
}
}
但是,稍后,当我在我的一个构建任务中访问定义的项目 属性 "customer"(其值在我当前正在构建的产品风格中设置)时,它总是有值 "c" 即使我正在构建客户 A(在这种情况下 属性 客户应该是 "a" 而不是 "c")。例如我稍后执行以下任务:
preBuild << {
println "Building customer: " + customer
}
它总是打印:
Building customer: c
所以我猜发生了一些覆盖?可能与配置 VS 执行阶段有关?虽然不确定 how/why,因此非常感谢您的帮助。
更新:或者,它已经让我进一步确定产品风味的名称(没有附加构建类型名称)和构建类型(再次:没有在 gradle 构建的执行阶段期间添加到它前面的产品风味名称。
考虑到上述配置,预期的产品口味名称将是:customerA、customerB 和 customerC。
在评估阶段,Gradle 执行您的 android
块中的所有代码;它不只是执行与您要编译的风格相关的代码。事实上,在评估阶段,它甚至不知道你的口味是什么;它必须对其进行评估才能找出答案。
所以你的所有三行 customer = "a"
、customer = "b"
和 customer = "c"
都将被执行。
这是 Gradle 的微妙之处之一,这使得学习起来有点困难。
所以我已经解释了为什么你的代码没有按照你期望的方式工作,但是这个答案是不完整的,因为我没有说很多关于如何做才能让它正常工作,但很难说该怎么做,因为我不确定您要完成什么。一般来说,我可以说你应该考虑尝试使用用户定义的任务来完成你想要的,并设置任务内依赖关系以确保事情以正确的顺序执行。 Android Gradle 构建的一个陷阱是,即使这些任务在评估阶段之前也不会被定义(在评估构建文件并知道之前,它无法知道构建所有风格所需的任务这些风格是什么),所以做一些 SO 调查,看看如何将事情挂钩到 Android Gradle 构建任务上——你必须在 [=22] 之后的评估阶段结束时设置你的任务=] 插件已经完成了它的工作。
非常感谢 Scott Barta,感谢他的建议和解释,为什么我的解决方案不起作用(这也让我重新考虑了一些事情)。我基本上想出了不同的方法来完成我需要的。
除非你需要做的事情不能通过简单地根据构建类型和风格(即通过约定)组织你的 Android 资源树来实现,否则我会推荐选项 2。虽然我确实保留了选项 1 仅供参考,因为它涵盖了 productFlavor 属性 扩展的有趣主题。
- 基于自定义 属性 的选项:Product Flavors 允许您定义自定义属性,从而扩展 productFlavor。 Xavier Ducrohet 在这里提供了一个示例:
我将提供一个与上面提供的非常简单且相似的示例,但在我的例子中我需要一个字符串 属性,而不是布尔值。
// 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 等。请参阅上面代码段的第一部分)。现在,根据每个客户是调试版本还是发布版本,将这个文件放入适当的文件夹中,例如 customerADebug --> 包含带有调试模式等服务器设置的文件。
例如,当您构建 customerA 时,如果您构建调试或发布版本,则会选择正确的文件。
回答我 post 的更新部分:
产品风格名称(不含 buildType):
flavor.name (where flavor is a productFlavor)
以下方法对我有用,可以将自定义属性添加到产品口味中:
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'
}
}
}
flavor1
具有自定义 属性 的默认值,而 flavor2
具有覆盖值。
这是一个如何访问自定义 属性:
的示例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]
}
我假设同样可以将自定义属性添加到构建类型,但我还没有对此进行测试。