Android Studio: Gradle产品风味:定义自定义属性

24

我正在使用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",即使我正在构建的是customerA(在这种情况下,属性customer应该是"a"而不是"c")。例如,我稍后执行以下任务:

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

并且它总是打印:

构建客户端:c

因此,我猜测可能发生了一些覆盖?可能与配置和执行阶段有关?不确定如何或为什么,因此任何帮助都将不胜感激。

更新:或者在Gradle构建的执行阶段确定产品风味(不附加生成类型名称)和构建类型(同样:不附加产品风味名称),这将让我进一步。

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

3个回答

19
在评估阶段,Gradle将执行您android块中的所有代码; 它不仅会执行与您要编译的风味相关的代码。 实际上,在评估阶段,它甚至不知道您的风味是什么; 它必须进行评估以找出。
因此,您的所有三行customer = "a"customer = "b"customer = "c"都将被执行。
这是关于Gradle的一个微妙之处,使其有点难以学习。
因此,我已经解释了为什么您的代码不能按您的期望工作,但是这个答案是不完整的,因为我没有说很多关于如何使其正确工作的内容,但很难说该怎么做,因为我不确定您想要实现什么。总的来说,我可以说您应该考虑尝试使用用户定义的任务来完成您想要的, 并设置任务内部依赖项以确保以正确的顺序执行事情。 Android Gradle构建的一个陷阱是即使这些任务也在评估阶段之后才会定义( 它只有在评估构建文件并知道这些风味是什么之后,才知道需要构建所有风味的任务),因此请进行一些SO拼图,以了解如何挂接事情到Android Gradle build任务上 - 您必须在Android插件完成其工作后的评估阶段结束时设置您的任务。

非常感谢您的回答!我会认真考虑,除非我想到了有用的东西,否则将其标记为正确答案:)。还有一件事:我在结尾处的“编辑”实际上是针对另一个SO问题的 - 我搞错了问题,对可能引起的混淆感到抱歉。 - AgentKnopf
您IP地址为143.198.54.68,由于运营成本限制,当前对于免费用户的使用频率限制为每个IP每72小时10次对话,如需解除限制,请点击左下角设置图标按钮(手机用户先点击左上角菜单按钮)。 - AgentKnopf
3
回答开头看起来非常有用,但最后一段话却使我原本的困惑更加深了。 - Richard Le Mesurier

12

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

除非您需要做的无法通过根据构建类型和Flavors组织Android资源树(即通过约定)实现,否则我建议选择选项2。尽管我为了参考目的保留了选项1,因为它涵盖了产品Flavor属性扩展的有趣主题。

  1. 自定义基于属性的选项:Product Flavors允许您定义自定义属性,从而扩展产品Flavor。Xavier Ducrohet在此处提供了一个示例:https://dev59.com/UGMm5IYBdhLWcg3wVdx5#17708357

我会提供一个非常简单且类似于上面提供的示例,但在我的情况下,我需要一个字符串属性,而不是布尔属性。

    // 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. 通过访问已经存在的产品口味属性 "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资源目录结构是根据实际的版本命名的。

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

  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

因此,每种产品风味的主要内容都在与风味同名的文件夹中(例如customerA,customerB等,请参见上面代码段的第一部分)。现在,这个文件(基于不同客户的debug或release构建而有所不同)将被放入相应的文件夹中,如customerADebug--> 包含调试模式下的服务器设置文件等。

当你构建customerA时,如果你构建的是debug或release版本,系统会自动选择正确的文件。

回答我的帖子更新部分:

产品风味名称(不包括buildType):

flavor.name(其中flavor是productFlavor)


答案发布于2015年2月-因此请注意可能会影响此回答的实用性的潜在更改。 - AgentKnopf

7
以下方法适用于我来为产品风味添加自定义属性:
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]
}

我认为同样的方法可以用来添加自定义属性到构建类型中,但我还没有测试过。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接