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

Age*_*opf 21 android gradle android-gradle-plugin

我正在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"
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

但是,稍后,当我在我的一个构建任务中访问定义的项目属性"customer"(其值在我正在构建的产品风格中设置)时,即使iam构建customerA,它也始终具有值"c"(在这种情况下,财产客户应该是"a"而不是"c").例如,我稍后执行以下任务:

preBuild << {
    println "Building customer: " + customer
}
Run Code Online (Sandbox Code Playgroud)

它始终打印:

建立客户:c

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

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

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

Sco*_*rta 18

在评估阶段,Gradle执行android块中的所有代码; 它不只是执行与您要编译的flavor相关的代码.事实上,在评估阶段,它甚至不知道你的口味是什么; 它必须评估,以找出答案.

所以你行的所有三个customer = "a",customer = "b"customer = "c"将得到执行.

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

所以我已经解释了为什么你的代码没有按照你期望的方式工作,但是这个答案是不完整的,因为我没有说很多关于如何使它正常工作,但很难说该怎么做因为我我不确定你要完成什么.一般来说,我可以说你应该考虑尝试使用用户定义的任务来完成你想要的任务,并设置任务内部依赖关系以确保以正确的顺序执行任务.使用Android Gradle构建的问题是,即使这些任务在评估阶段之前也无法定义(在评估构建文件并知道这些风格是什么之前,它无法知道构建所有风格所需的任务),

  • 答案开始看起来非常有用,但最后一段只会加剧我已经存在的困惑. (3认同)

Age*_*opf 11

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

除非你只需要根据构建类型和风格(即通过约定)组织你的Android资源树就无法实现,所以我建议选项2.虽然我确实保留了选项1以供参考,因为它涵盖了productFlavor属性扩展的有趣主题.

  1. 基于自定义属性的选项:Product Flavors允许您定义自定义属性,从而扩展productFlavor.Xavier Ducrohet在此提供了一个示例:https://stackoverflow.com/a/17708357/1041533

我将提供一个如上所述的非常简单和类似的示例,但在我的情况下,我需要一个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

            }
        }
    }
Run Code Online (Sandbox Code Playgroud)

我很清楚,以上是完全没必要的,因为我想要的只是我的味道的名称(没有其中的构建类型),一旦我找到了给我的味道名称的属性,我就能够改变所有以上代码如下:

  1. 只需访问已存在的名为"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
    
            }
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)

上面的内容也更有意义,因为Android资源的目录结构是以实际的风格命名的.

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

  1. 基于资源目录的方法

打算根据是否是发布版本或调试版本修改每个客户的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
Run Code Online (Sandbox Code Playgroud)

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

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

要回答我帖子的UPDATE部分:

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

flavor.name(其中flavor是productFlavor)


ash*_*hes 6

以下内容对我来说可以为产品风味添加自定义属性:

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'
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

flavor1具有custom属性的默认值,而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]
}
Run Code Online (Sandbox Code Playgroud)

我假设可以执行相同的操作以将自定义属性添加到构建类型中,但是我尚未对此进行测试。