使用Build Flavors – 正确地构造源文件夹和build.gradle

请注意:在Xavier的答案后编辑的答案

我正在尝试在Android Studio中为一个相同的应用程序项目使用不同的构build风格 。 但是,我似乎有一个可怕的时间configuration它适当地工作。

脚步:

  1. 创build一个名为“Test”的新的Android Studio项目。
  2. 打开build.gradle *并添加以下几行:

    productFlavors { flavor1 { packageName 'com.android.studio.test.flavor1' } flavor2 { packageName 'com.android.studio.test.flavor2' } } 
  3. 重新启动Android Studio之后,我现在可以在Build Variants部分下看到4个构build变体。 这意味着我们迄今为止在产品口味方面取得了成功。 **
  4. flavor1创build一个新的Source文件夹; 但是,我不确定我是否以正确的方式进行。 以下是我如何做到的:

    • 请记住,这个项目的包名是: com.foo.test
    • 右键单击src文件夹,对于flavor1,我实际上是在资源pipe理器中创build了个别文件夹,其结构是src/flavor1/java/com/foo/test/MainActivity.java
    • 上面的工作很好,因为'java'文件夹是蓝色的 ,这意味着IDE知道它的一个活动的源目录。 另外,这个包是自动创build的。 尽pipe如此,我发现重复类的警告。 在这里看到截图。
    • 对于flavor2,我尝试手动创build软件包,但是用于flavor2的'src'文件夹似乎不是蓝色的,因此当右键单击时选项是不同的,'New Package'不能用于我。 在这里看到图片。
    • 请注意,对于flavor1,我还创build了一个“res”目录,该目录变成蓝色,但是尽pipe如此,如果我想使用不同的方法,则不能创buildAndroid资源文件或Andorid资源目录为不同的口味提供余地。

难道我做错了什么? 还是我错过了什么? 让我知道如果你需要更多的信息。

*我的项目似乎有两个 build.gradle文件。 一个位于项目文件夹(\ GradleTest)的根目录下,这个是空的。 第二个位于\ GradleTest的子文件夹的根目录,也标记为“GradleTest”(GradleTest-GradleTest),这是打开时已经有代码的那个; 因此,那是我编辑的那个。

**我检查gradle设置,显然使用自动导入 已经启用。 尽pipe如此,更改build.gradle文件不会自动更新构build变体。 注意:我也试过使用Build – Rebuild Project和/或Build – Make Project,不行。 我仍然必须closures项目,并重新开放更改才能生效。

如果你在Studio偏好设置中,在Gradle部分下,你可以为你的项目启用自动导入(我们将在以后默认启用它)。 这会让Studio在您编辑时重新导入您的build.gradle。

创build口味并不意味着你要使用自定义代码,所以我们不创build文件夹。 你确实需要自己创build它们。

如果你看看我的IO说话,你会看到我们如何混合来自风味和构buildtypes的值来创build变体。

对于Java来源:

 src/main/java src/flavor1/java src/debug/java 

都是用来创build一个单一的输出。 这意味着他们不能定义相同的类。

如果你想在这两种风味中有相同类别的不同版本,你需要在两种风格中创build它。

 src/flavor1/java/com/foo/A.java src/flavor2/java/com/foo/A.java 

然后你的代码在src / main / java中可以做到

 import com.foo.A 

根据所select的风味,使用com.foo.A的正确版本。

这也意味着两个版本的A必须具有相同的API(至less当涉及到src / main / java /中的类使用的API …

编辑以匹配修改后的问题

另外,仅在相互排斥的源文件夹中放置相同的A类非常重要。 在这种情况下,src / flavor1 / java和src / flavor2 / java从不一起被select,但是main和flavor1是。

如果你想提供不同风格的不同版本的活动,不要把它放在src / main / java中。

请注意,如果您有3种口味,只需要为flavor1定制一个,而flavor2和flavor3共享相同的活动,则可以为这两个其他活动创build一个通用的源文件夹。 您可以灵活地创build新的源文件夹并将源集configuration为使用它们。

在你的其他方面:

第二个味道源文件夹不是蓝色是正常的。 你需要切换到第二种风格来启用它,然后你就可以在里面创build包和类。 在此之前,Studio并不认为它是源文件夹。 我们希望将来可以改进这一点,使IDE知道那些无效的源文件夹。

我认为在res文件夹中不能创build资源文件也是正常的。 菜单系统尚未更新,以处理所有这些额外的资源文件夹。 这会晚一点。

Android上的“产品风味”

我有时被问到如何处理不同的主机,图标甚至软件包名称,并在同一个应用程序的不同版本上取消。

有很多原因可以做到这一点,一个简单的方法去:产品味道。

你可以在你的build.gradle脚本中定义前面描述过的这些东西。

产品口味本文的一部分是关于产品口味的书面思考,那么它们是什么? 关于Android文档:

产品风格定义了项目构build的应用程序的定制版本。 单个项目可以有不同的风格来改变生成的应用程序。

你怎么定义它们? 你必须写在你想要定义的风格的build.gradle上:

 productFlavors { ... devel { ... } prod { ... } } 

现在,我们将有两种不同的风格的我们的应用程序。 您也可以在Build Variants选项卡中的Android Studio上进行检查

build立变体

多个包名称

如果你想在你的手机上安装一个具有开发状态的应用程序和一个用于生产状态的应用程序。 您可能知道,只能安装一个具有相同软件包名称的应用程序(如果您尝试安装与手机上安装的APK相同的新APK,它将尝试更新它)。

您唯一需要做的就是在每种产品口味上定义它:

 android { productFlavors { devel { applicationId "zuul.com.android.devel" } prod { applicationId "zuul.com.android" } } } 

根据风格向多个主机发送请求像以前一样,您必须在产品风格configuration字段中包含一些参数。

 android { productFlavors { devel { applicationId "zuul.com.android.devel" buildConfigField 'String', 'HOST', '"http://192.168.1.34:3000"' } prod { applicationId "zuul.com.android" buildConfigField 'String', 'HOST', '"http://api.zuul.com"' } } } 

作为一个例子,我们将尝试向您展示如何将此与Retrofit集成,以将请求发送到合适的服务器,而无需处理您指定的服务器以及基于风格。 在这种情况下,这是Zuul android应用程序的摘录:

 public class RetrofitModule { public ZuulService getRestAdapter() { RestAdapter restAdapter = new RestAdapter.Builder() .setEndpoint(BuildConfig.HOST) .setLogLevel(RestAdapter.LogLevel.FULL) .build(); return restAdapter.create(ZuulService.class); } } 

正如你所看到的,你只需要使用BuildConfigclass来访问你刚定义的variables。

任何通过代码可用的variablesHOSTvariables并不是您可以在代码中公开的唯一variables。 你可以随心所欲地做到这一点:

 prod { applicationId "zuul.com.android" buildConfigField 'String', 'HOST', '"http://api.zuul.com"' buildConfigField 'String', 'FLAVOR', '"prod"' buildConfigField "boolean", "REPORT_CRASHES", "true" } 

您可以按如下方式访问它们:

 BuildConfig.HOST BuildConfig.FLAVOR BuildConfig.REPORT_CRASHES 

每种风味的不同图标如果你想要每种风味有不同的图标,所以你可以直观地检测到你打开的是哪一个(你可以用名字来做它,但它不适合这个空间!),你只需要为每种口味定义新的目录结构。

在我刚刚使用的例子中,有两种口味:devel和prod。 然后,我们可以定义一个两个新的目录结构,以便我们可以定义我们想要的资源:

结构体

这可以与其他types的资源,如strings.xml, integers.xml, arrays.xml等。

configuration签名设置

要使用Gradle构buildconfiguration为您的发布版本types手动configuration签名configuration:

1.创build一个密钥库。 密钥库是一个包含一组私钥的二进制文件。 您必须将您的密钥库保存在安全可靠的地方。 2.创build一个私钥。 私钥表示要与应用标识的实体,如个人或公司。 3.将签名configuration添加到模块级build.gradle文件中:

 android { ... defaultConfig {...} signingConfigs { release { storeFile file("myreleasekey.keystore") storePassword "password" keyAlias "MyReleaseKey" keyPassword "password" } } buildTypes { release { ... signingConfig signingConfigs.release } } 

}

生成签名的APK:

要生成已签名的APK,请从主菜单中select“生成”>“生成已签名的APK”。 app / build / apk / app-release.apk中的包现在使用您的发行版密钥进行签名。

ref: https : //developer.android.com/studio/build/build-variants.html#signing,http : //blog.brainattica.com/how-to-work-with-flavours-on-android/

看来你需要在build.gradle添加新的风格后重新加载你的项目 。 之后,您将在Build Variants视图中看到4个构build变体(您可以从窗口的左边缘访问它)。

关于额外的源代码目录,似乎你需要手工创build它们: src/flavor1/javasrc/flavor2/java 。 您将看到,在“Build Variants”视图中更改风格将改变当前活动的源目录 (当它是活动源目录时,目录是蓝色的)

最后,“gradle将为您的新口味创build新的sourceSets”意味着gradle将创build对象android.sourceSets.flavor1android.sourceSets.flavor2并且您可以在您的build.gradle脚本中使用它们。 但是这些对象是dynamic创build的,这就是为什么你不在build.gradle看到它们(我build议你阅读这个: http : //www.gradle.org/docs/current/userguide/tutorial_using_tasks.html特别是6.6:它解释了dynamic任务的创build,一个gradle脚本是一个groovy脚本,所以我build议你也熟悉groovy)

当我将项目迁移到Gradle时,我遇到了同样的问题。 问题是,构build没有find适当的资源文件夹。 我通过在build.gradle中的android元素下添加这个来修复它:

 sourceSets { main { res.srcDirs = ['myProject/res'] } } 

一些重要的东西阻塞了我很长一段时间,味道的名字,需要匹配的包,而不是在Gradle中定义的风格定义的包。 例如:

src/flavor1/java/com/foo/A.java

将匹配

 productFlavors { flavor1 { packageName 'com.android.studio.test.foobar' } } 

src/foobar/java/com/foo/A.java不会被用于flavor1构build。

在gradle中:

对于构buildtypes,您只需要:

 buildTypes { release{ //proguard, signing etc. } debug { //development } } } 

然后为口味添加你需要的口味

 productFlavors { pro { applicationIdSuffix '.paid' buildConfigField 'boolean', 'PRO', 'true' } free { applicationIdSuffix '.free' buildConfigField 'boolean', 'PRO', 'false' } }