使用构建风格 - 正确组织源文件夹和build.gradle

174
请注意:Xavier回答后,我编辑了答案。
我正在尝试在Android Studio中为同一个应用程序项目使用不同的Build Flavors。然而,我似乎无法很好地配置它以使其正常工作。
步骤:
  1. Create a new Android Studio Project, named 'Test'.
  2. Open build.gradle* and added the following lines:

    productFlavors {
    flavor1 {
        packageName 'com.android.studio.test.flavor1'
        }
    flavor2 {
        packageName 'com.android.studio.test.flavor2'
        }
    }
    
  3. After restarting Android Studio, I now see 4 build variants under the Build Variants section. Meaning we were succesful on seting up the product flavors so far. **
  4. Created a new Source folder for flavor1; however, I am not sure if I'm doing it the right way. Here's how I did it:

    • Keep in mind that my Package name for this project is: com.foo.test
    • Right click on the src folder, for flavor1, I actually created the individual folders in the explorer, in a way that the structure is src/flavor1/java/com/foo/test/MainActivity.java.
    • The above worked well, since the 'java' folder is in blue, meaning the IDE knows its an active source directory. Also, the package was automatically created. Despite of this, I am getting a warning for duplicate class found. See screenshot here.
    • For flavor2, I tried creating the package manually, but 'src' folder for flavor2 seems not be in blue, and therefore the options are different when right clicked, and 'New Package' is not available for me to use. See image here.
    • Note that for flavor1, I also created a 'res' directory, which does turn blue, but despite of that, doesn't offer the ability to create either an Android Resource file, or Andorid resource directory, in case I wanted to use different resoruces for different flavors.

我是否做错了什么?或者我漏掉了什么?如果需要更多信息,请告诉我。

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

**我检查了gradle设置,显然使用自动导入已经启用。尽管如此,对build.gradle文件进行更改不会自动更新构建变体。注意:我也尝试使用Build - Rebuild Project和/或Build - Make Project,但都无法实现。我仍然必须关闭项目,并重新打开才能使更改生效。


请注意,现在支持使用applicationId替代packageName - Hamzeh Soboh
7个回答

227

在Studio的“Gradle”部分中,如果您查看偏好设置,您可以为项目启用自动导入(我们稍后将默认启用此功能)。这样一来,每当您编辑build.gradle文件时,Studio就会重新导入它。

创建变体并不意味着您将为它们使用自定义代码,因此我们不会创建文件夹。您需要自己创建它们。

如果您观看我的IO演讲,您将看到我们如何混合使用变体和构建类型的值来创建变种。

对于Java源代码:

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

这三个都用于创建单个输出,这意味着它们不能定义相同的类。

如果您想在两种版本中拥有相同类的不同版本,则需要在两个版本中都创建它。

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

然后,您在src/main/java中的代码可以执行

import com.foo.A

根据所选的口味,选择正确版本的com.foo.A。

这也意味着A的两个版本必须具有相同的API(至少在src/main/java/中使用的API方面)。

编辑以匹配修改后的问题

此外,在仅彼此排他的源文件夹中放置相同的A类非常重要。 在这种情况下,src/flavor1/java和src/flavor2/java永远不会同时被选择,但是main和fl avor1会被选择。

如果您想为不同的风味提供不同版本的activity,请勿将其放入src/main/java中。

请注意,如果您有3种风味并且仅想为flavor1提供自定义,则flavor2和flavor3共享相同的activity,可以为这两个其他activity创建通用源文件夹。 您可以完全灵活地创建新的源文件夹并配置源集以使用它们。

到您的其他要点:

第二个风味源文件夹不是蓝色是正常的。 您需要切换到第二种口味才能启用它,然后您将能够在其中创建包和类。 在此之前,Studio不认为它是源文件夹。 我们希望将来改进这一点,使IDE意识到这些“未激活”的源文件夹。

我认为您无法在res文件夹中创建资源文件也是正常的。 菜单系统尚未更新以处理所有这些额外的资源文件夹。 这将稍后推出。


1
我在我的答案末尾添加了一些新元素,但是重复是有意义的。你不能在src/main/java和src/flavor1/java中都有相同的类,因为两者在选择flavor1时都会被使用。请注意,在我的答案中,我将相同的类仅放在flavor1/java和flavor2/java中,因为它们是互斥的,永远不会同时启用。 - Xavier Ducrohet
3
您可以进行每个flavor的依赖关系配置。使用flavorCompile ...即可。 - Xavier Ducrohet
@XavierDucrohet 我已经更新了原始问题,并附上了所有最新的尝试,请在有空的时候查看。谢谢!https://dev59.com/7oDba4cB1Zd3GeqPK_V5 - Valerio Santinelli
如何为相同的活动创建一个公共源?你们有没有样例可以分享给我? - Mustafa Güven
@XavierDucrohet - 你能告诉我们是否可以根据构建风格替换jniLibs资源吗?https://dev59.com/NY3da4cB1Zd3GeqP04Kj.. 非常感谢! - user2511882
显示剩余6条评论

19

Android中的“产品风味”

有时人们问我如何在同一应用程序的不同版本中使用不同的主机、图标甚至包名。

有很多理由可以这样做,而其中一个简单的方法就是使用“产品风味”。

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

产品风味 本文的一部分是针对产品风味编写的,那么什么是产品风味?根据Android文档:

产品风味定义了项目构建的自定义应用程序版本。单个项目可以有不同的风味,这些风味会改变生成的应用程序。

你如何定义它们?你必须在build.gradle文件中写出要定义哪些风味:

productFlavors {  
        ...
        devel {
            ...
        }

        prod {
            ...
        }
    }

现在,我们将有两种不同的应用程序版本。您也可以在Android Studio中的“Build Variants”选项卡中检查。

构建变体

多个包名

如果您想在手机上安装一个处于开发状态的应用程序和一个处于生产状态的应用程序怎么办?正如您所知,您只能安装一个具有相同包名的应用程序(如果您尝试安装一个与已安装在手机上的应用程序包名相同的新APK,则会尝试更新该应用程序)。

您需要做的唯一一件事情就是在每个产品风格中定义它:

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

根据口味向多个主机发送请求。 与之前一样,您必须在产品口味配置字段中包含一些参数。

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集成,以便在不处理指向哪个服务器的情况下,基于flavor发送请求到适当的服务器。在此示例中,这是Zuul安卓应用程序的摘录:

public class RetrofitModule {

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

}

如您所见,您只需使用BuildConfig类来访问刚刚定义的变量。

任何变量都可以通过您的代码使用。HOST变量并不是您可以在代码中公开的唯一变量。您可以自由使用您需要公开的变量。

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。然后,我们可以定义两个新的目录结构,以便我们可以定义所需的资源:

结构

这适用于其他类型的资源,如strings.xml,integers.xml,arrays.xml等。

配置签名设置

要手动使用Gradle构建配置为发布版本构建类型配置签名配置:

1.创建密钥库。密钥库是包含一组私钥的二进制文件。您必须将密钥库放在安全的地方。 2.创建私钥。私钥表示应用程序要识别的实体,例如人或公司。 3.将签名配置添加到模块级build.gradle文件中:

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

}

生成已签名的APK:

要生成已签名的APK,请从主菜单中选择Build > Generate Signed APK。现在,在app/build/apk/app-release.apk包中,该应用程序已使用您的发布密钥进行了签名。

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


2
这是同一个网址吗?http://blog.brainattica.com/how-to-work-with-flavours-on-android/ - Sreehari

7
似乎您需要在 build.gradle 中添加新的 flavor 后重新加载项目。然后,您将在“Build Variants”视图中看到 4 个构建变体(可以从窗口左侧访问该视图)。
关于额外的源目录,似乎您需要手动创建它们:src/flavor1/java 和 src/flavor2/java。您会发现,在“Build Variants”视图中更改 flavor 将更改当前活动的源目录(当目录是“活动源目录”时,目录将变为蓝色)。
最后,“gradle will create new sourceSets for your new flavors” 意味着 gradle 将创建对象 android.sourceSets.flavor1 和 android.sourceSets.flavor2,并且您可以在 build.gradle 脚本中使用它们。但是这些对象是动态创建的,这就是为什么您不会在 build.gradle 中看到它们的原因 (我建议您阅读此文档:http://www.gradle.org/docs/current/userguide/tutorial_using_tasks.html 特别是 6.6 部分,它解释了动态任务的创建。Gradle 脚本是 Groovy 脚本,因此建议您熟悉一下 Groovy)。

2
我认为重要的是Build Variants视图,我没有注意到。 - Chris.Jenkins

2

当我将我的项目迁移到Gradle时,我遇到了相同的问题。问题是构建未找到正确的资源文件夹。我通过在build.gradle中的android元素下添加以下内容来解决它:

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

0

有一件事情困扰了我很长时间,那就是口味名称需要与包名匹配,而不是与Gradle中口味定义内部定义的包名匹配。例如:

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

将匹配

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

但是

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


0

在gradle中配置flavors之后,为了让react-native查看项目根目录中的自定义.env文件,您必须添加到android/app/build.gradle中。

在我的示例中,我正在使用相同的代码库为Android设备创建两个应用程序,一个用于group1,另一个用于group2。

//将此添加到app/build.gradle的第3行

project.ext.envConfigFiles = [
projecta: ".env.development.android.projecta",
projectb: ".env.development.android.projectb",
]

//别忘了添加

android {
compileSdkVersion rootProject.ext.compileSdkVersion
flavorDimensions “default// add this line
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8

}

然后将此数组中的键与您的产品风味相匹配

  productFlavors {
    project1 {
        minSdkVersion rootProject.ext.minSdkVersion
        applicationId 'com.nativeapp.project1'
        targetSdkVersion rootProject.ext.targetSdkVersion
        resValue "string", "build_config_package", "com.nativeapp"
    }
    project2 {
        minSdkVersion rootProject.ext.minSdkVersion
        applicationId 'com.nativeapp.staff'
        targetSdkVersion rootProject.ext.targetSdkVersion
        resValue "string", "build_config_package", "com.nativeapp"
    }
}

还要确保applicationId和resValue以您的应用程序包名称开头

在我的情况下,它是“nativeapp”,但您可以在MainActivity.java文件中找到自己的。

如果您深入挖掘android/app/main/java/com/目录,就可以找到它。

之后,您可以根据需要配置脚本,使其看起来像这样

"scripts": {
"android:project1": "ENVFILE=.env.development.candidate react-native run-android --variant=projectaDebug --appIdSuffix=projecta",
"android:staff": "ENVFILE=.env.development.staff.android react-native run-android --variant=projectbDebug --appIdSuffix=projectb",}

注意:添加脚本时,变体必须是口味名称,例如“project2”后跟“Debug”=“projectb”,因为这是您的默认构建类型。
此外,请确保在android/app/src/中为每个口味创建了相关文件夹。
在我的情况下。
android/app/src/projecta/
android/app/src/projecta/

如果一切设置正确,使用您创建的脚本构建时,它应该会创建正确的 res 文件夹和 Android 清单文件。

如果您想更改每个应用程序实例的名称,请更改 res/values 目录中的 strings.xml。

<resources>
<string name="app_name">Project1</string> //this will be the name of your app when its built in your emulator
</resources>

-1

在gradle中:

对于构建类型,您只需要:

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

然后对于口味,您添加所需的口味

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

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