Android Studio:Gradle Product Flavors:定义自定义属性

时间:2015-01-30 08:52:53

标签: android gradle android-gradle

我正在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”(其值在我正在构建的产品风格中设置)时,即使iam,它始终具有值“c”建立customerA(在这种情况下,财产客户应该是“a”而不是“c”)。例如,我稍后执行以下任务:

preBuild << {
    println "Building customer: " + customer
}

并始终打印:

  

建立客户:c

所以我猜猜有一些覆盖发生?可能与配置VS执行阶段有关?不知道如何/为什么,所以任何帮助都将非常感激。

更新:或者它已经让我更进一步确定产品风味的名称(没有附加的构建类型名称)和构建类型(再次:没有产品风味名称前置在gradle构建的执行阶段。

考虑到上述配置,预期的产品风味名称将是:customerA,customerB和customerC。

3 个答案:

答案 0 :(得分:18)

在评估阶段,Gradle会执行android块中的所有代码;它并不只是执行与您想要编译的风格相关的代码。事实上,在评估阶段,它甚至不知道你的口味是什么;它必须对此进行评估才能找到答案。

因此,您的所有三行[{1}},customer = "a"customer = "b"都会被执行。

这是关于Gradle的一个微妙的事情,使它有点难以学习。

所以我已经解释了为什么你的代码没有按照你期望的方式工作,但是这个答案是不完整的,因为我还没有说明要做些什么来使它正常工作,但是很难说该做什么,因为我不确定你想要完成什么。一般来说,我可以说你应该考虑尝试使用用户定义的任务来完成你想要的任务,并设置任务内部依赖关系以确保以正确的顺序执行任务。使用Android Gradle构建的问题是,即使这些任务在评估阶段之前也无法定义(它无法知道构建所有风格所需的任务,直到它评估构建文件并知道那些风格是什么),所以做一些SO调查看看如何将事情挂钩到Android Gradle构建任务 - 你必须在Android插件完成它之后在评估阶段结束时设置你的任务。

答案 1 :(得分:11)

非常感谢斯科特巴塔,他的建议和解释,为什么我的解决方案不起作用(这也让我重新考虑了一些事情)。我基本上想出了不同的方法来完成我需要的工作。

除非你只需要根据构建类型和风格(即通过惯例)组织你的Android资源树来实现你需要做的事情,否则我建议选项2.虽然我确实保留了选项1仅供参考,因为它涵盖了productFlavor属性扩展的有趣主题。

  1. 基于自定义属性的选项:Product Flavors允许您定义自定义属性,从而扩展productFlavor。 Xavier Ducrohet在此提供了一个例子:https://stackoverflow.com/a/17708357/1041533
  2. 我将提供一个如上所述的非常简单和类似的示例,但在我的情况下,我需要一个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
    
                }
            }
        }
    

    然后我意识到,上面的内容完全是不必要的,因为我想要的只是我的味道的名称(没有构建类型)并且一旦我找到了给我名字的属性,我就能够更改以上所有代码如下:

    1. 只需使用您的风味名称作为客户的名称,方法是访问名为&#34; name&#34;的已存在的产品风味属性。

      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
      
              }
          }
      }
      
    2. 上面的内容也更有意义,因为Android资源的目录结构是以实际风味命名的。

      后者也引导我找到原始问题的最终解决方案:

      1. 基于资源目录的方法
      2. 目的是根据每个客户的版本或调试版本修改每个客户的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
        

        因此,每种产品风味的主要内容都在与风味名称相同的文件夹中(customerA,customerB等。请参阅上面代码段的第一部分)。现在这个根据是否是每个客户的调试或发布版本而不同的文件被放入相应的文件夹,例如customerADebug - &gt;包含调试模式等服务器设置的文件。

        例如,当您构建customerA时,如果您构建调试或发布版本,将选择正确的文件。

        要回复帖子的更新部分:

        产品风味名称(不含buildType):

          

        flavor.name(其中flavor是productFlavor)

答案 2 :(得分:4)

以下是我为产品风格添加自定义属性的方法:

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]
}

我假设可以为构建类型添加自定义属性,但我没有对此进行测试。