当创建了一个新的 build type 之后, Gradle 也会创建一个新的 source set. 默认的 source set 目录会放在相同的 Build Type 的目录下. 当你创建一个新的 build type 时, 该目录不会自动创建, 你必须在你使用代码与资源前自己为每一个 build type 创建 source set 目录.
三种 buildType 的目录结构如下:
App
└── src
├── debug
│ ├── java
│ │ └── com.package
│ ├── res
│ │ └── layout
│ │ └── activity_main.xml
│ └── AndroidManifest.xml
├── main
│ ├── java
│ │ └── com.package
│ ├── res
└── MainActivity.java
└── Constants.java
│ └── AndroidManifest.xml
├── custom
│ ├── java
│ │ └── com.package
├── drawable
└── layout
└── activity_main.xml
│ ├── res
│ │ └── layout
│ │ └── activity_main.xml
│ └── AndroidManifest.xml
└── release
├── java
│ └── com.package
│ └── Constants.java
└── AndroidManifest.xml
假如我们自己建立 custom 的 source set
我们使用不同的构建版本便会使用不同的 source set. 当使用不同的 source sets 的时候, 资源文件的处理需要特殊的方式. Drawables 和 layout 文件将会复写在 main 中的重名文件, 但是 values 文件下的资源不会. gradle 将会把这些资源连同 main 里面的资源一起合并.(如果出现资源重复异常, 请 clean 一下工程)
例如, 在 main 中的 string.xml 为:
- <resources>
- <string name="app_name">BuildTypeProject</string>
- <string name="hello_name">BuildTypeHello</string>
- </resources>
在 custom 版本中为:
- <resources>
- <string name="app_name">BuildTypeCustomProject</string>
- </resources>
当我们构建 custom 版本的时会合并为:
- <resources>
- <string name="app_name">BuildTypeCustomProject</string>
- <string name="hello_name">BuildTypeHello</string>
- </resources>
当你创建一个新的构建版本而不是 custom, 最终的 strings.xml 将会是 main 目录下的 strings.xml.
manifest 也和 value 文件下的文件一样. 如果你为你的构建版本创建了一个 manifest 文件, 那么你不必要去拷贝在 main 文件下的 manifest 文件, 你需要做的是添加标签. Android 插件将会为你合并它们.
但是需要注意, 当我们添加. java 文件到 custom 版本中, 你可以添加相同的类到 debug 和 release 版本, 但是不能添加到 main 版本. 如果你添加了, 会抛出异常. 这时候我们如果构建 custom 版本, 那么便会使用 custom 对应 source set 中的. java 文件.
依赖包
每一个构建版本都有自己的依赖包, gradle 自动为每一个构建的版本创建不同的依赖配置. 如果你想为 debug 版本添加一个 logging 框架, 你可以这么做:
- dependencies {
- compile fileTree(dir: 'libs', include: ['*.jar'])
- compile 'com.android.support:appcompat-v7:22.2.0'
- debugCompile 'de.mindpipe.android:android-logging-log4j:1.0.3'
- }
- product flavors
不同的生产版本.
product flavors 极大简化了基于相同的代码构建不同版本的 App.
创建 product flavors
- Android {
- productFlavors {
- vivo {
- applicationId "vivo"
- versionCode 1
- minSdkVersion 15
- }
- oppo {
- applicationId "oppo"
- versionCode 2
- minSdkVersion 15
- }
- }
- }
这时候 AS 3.0 以上会报错 ERROR: All flavors must now belong to a named flavor dimension
没有给它们设置一个风味维度, 我们可以加上
- flavorDimensions "default"
- flavorDimensions "default"
- Android {
- productFlavors {
- vivo {
- applicationId "vivo"
- versionCode 1
- minSdkVersion 15
- }
- oppo {
- applicationId "oppo"
- versionCode 2
- minSdkVersion 15
- }
- }
- }
给这些产品版本默认一个风味维度, 具体有何作用, 等会会讲.
这时候我们会在左下角的窗口看到这么多个变体:
如果找不到该窗口, 可以在这里打开:
由于我们之前在 custom 构建版本上设置了 applicationIdSuffix ".custom", 所以, 当我们运行 oppoCustom 版本的时候, applicationId 为 oppo.custom
Source Set
product Flavors 也有自己的代码文件夹. 创建一个特殊的版本就像创建一个文件夹那么简单. 如下图所示:
但是值得注意的是, 我们无法再 vivo 文件夹里相同的包中添加构建版本已经有的. java 文件, 添加了会报异常. 而对于资源文件, 我们可以添加构建版本里面有的文件, 但是这个产品版本所对应的目录的优先级低于 Build type, 也就是说, 当 Custom 目录有一张图片, vivo 目录也有一张图片, 那么当我们运行打包 vivoCustom 版本的时候, 使用的是 custom 里面的图片. 除非我们建立的文件夹是 vivoCustom, 那么它的优先级便会是最高的.
Multiflavor variants
在某些情况下, 你可能希望创建一些联合的 Product Flavors, 这个时候便要使用到我们刚刚所说的 flavorDimensions 了.
设想一下, 假如我们需要打包两个渠道的 App:vivo 和 oppo, 而这两个渠道的 App 各自有付费版与免费版, 那么我们就需要用到多维度了.
首先定义两个维度: 渠道 channel, 付费与免费: money
- flavorDimensions "channel","money"
- flavorDimensions "channel","money"
- Android {
- productFlavors {
- vivo {
- dimension "channel"
- applicationId "vivo"
- versionCode 1
- minSdkVersion 15
- }
- oppo {
- dimension "channel"
- applicationId "oppo"
- versionCode 2
- minSdkVersion 15
- }
- free {
- dimension "money"
- }
- vip {
- dimension "money"
- }
- }
- }
当你添加了 flavor dimensions, 你就需要为每个 flavor 添加 dimension, 否则会提示错误.
之后我们可以看到这么多个变体:
而我们定义多个维度的顺序是很重要的, 因为当你在各个维度各自定义了同一个常量的值, 比如: buildConfigField("String", "name","\"custom app\""), 总是以第一维度的为准.
Build variants
构建变体
构建变体是构建版本和生产版本的结合体. 当你创建了一个构建版本或者生产版本, 同样的, 新的变体也会被创建.
像我们上图便有这么多的变体:
我们可以在这个窗口进行切换, 然后运行不同的变体.
tasks
Android Plugin 会为每一个配置的 Build Variant 创建 Tasks. 一个新的 Android App 拥有 Debug 和 Release 两种 Build Types, 所以默认的就会有两个 Task, 一个是 assembleDebug 一个是 assembleRelease 来构建不同的 APK. 当添加一个新的 Build Type 的时候, 一个新的 Task 也就会被创建, 一旦你开始添加 Flavors, 一整套 Tasks 就会被创建, 因为每一个 BuildType 的 Tasks 都会为每个 Product Flavor 联合.
Source sets
构建变体也可以有自己的资源文件夹, 举个例子, 你可以有 src/vivoVipCustom/java/. 原理与上面的类似
Resource and mainfest merging
Android Plugin 需要在打包前对 Main 的 SourceSet 以及 BuildType 的 SourceSet 进行一次 Merge. 而且 Library 工程也会提供额外的资源, 它们也会被 Merge, 例如 Manifest.xml 等等. 也会在其中声明一些权限等.
Resource 和 Manifest.xml 的优先级顺序如下:
如果一个资源在 main 中和在 flavor 中定义了, 那么那个在 flavor 中的资源有更高的优先级. 这样那个在 flavor 文件夹中的资源将会被打包到 apk. 而在依赖项目申明的资源总是拥有最低优先级.
当然, 如果你建立的目录是变体的目录入: vivoVipCustom, 那么它的优先级自然是高于 Build type
创建构建变体
关于如何构建变量, 上面已经说了, 不再重复.
- flavorDimensions "channel","money"
- Android {
- productFlavors {
- vivo {
- dimension "channel"
- applicationId "vivo"
- versionCode 1
- minSdkVersion 15
- }
- oppo {
- dimension "channel"
- applicationId "oppo"
- versionCode 2
- minSdkVersion 15
- }
- free {
- dimension "money"
- resValue "color", "colorfree", "#ff8888"
- }
- vip {
- dimension "money"
- resValue "color", "colorfree", "#ff0000"
- }
- }
- }
resValue "color", "colorfree", "#ff8888" 表示添加颜色名为 colorfree 的颜色
变体过滤器
忽略某个变体也是可行的. 这样你可以加速你的构建当使用 assemble 的时候, 这样你列出的 tasks 将不会执行那么你不需要的变体. 你可以使用过滤器, 在 build.gradle 中添加代码如下所示:
- Android.variantFilter { variant ->
- if(variant.buildType.name.equals('release')) {
- variant.getFlavors().each() { flavor ->
- if (flavor.name.equals('vivo')) { variant.setIgnore(true);
- }
- }
- }
- }
发现相关的变体不见了:
Signing Configurations
在你发布你的应用之前, 你需要为你的 App 私钥签名. 如果你有付费版和免费版, 你需要有不同的 key 去签名不同的变体. 这就是配置签名的好处. 配置签名可以这样定义:
- Android {
- signingConfigs {
- custom.initWith(signingConfigs.debug)
- release {
- storeFile file("release.keystore")
- storePassword"secretpassword"
- keyAlias "gradleforandroid"
- keyPassword "secretpassword"
- }
- }
- }
在这个例子中, 我们创建了 2 个不同的签名配置. debug 配置是 as 默认的, 其使用了公共的 keystore 和 password, 所以没有必要为 debug 版本创建签名配置了. custom 配置使用了 initWith() 方法, 其会复制其他的签名配置. 这意味着 custom 和 debug 的 key 是一样的.
release 配置使用了 storeFile, 定义了 key alias 和密码. 当然这不是一个好的选择, 你需要在 Gradle properties 文件中配置.
当你定义了签名配置后, 你需要应用它们. 构建版本都有一个属性叫做 signingConfig, 你可以这么干:
- Android {
- buildTypes {
- release {
- signingConfig signingConfigs.release
- }
- }
- }
上例使用了 buildTypes, 但是你可能需要对每个版本生成不同的验证, 你可以这么定义:
- Android {
- productFlavors {
- vivo{
- signingConfig signingConfigs.release
- }
- }
- }
当签名一个 Flavor 版本的时候, 你需要重写 BuildType 中的签名配置. 当需要使用相同的 BuildType 不同版本的 Flavors 的签名时, 可以通过下述方式:
- Android {
- buildTypes {
- release {
- productFlavors.vivo.signingConfig signingConfigs.vivo
- productFlavors.oppo.signingConfig signingConfigs.oppo
- }
- }
- }
上面这个例子展示了如何在 vivo 和 oppo 的 Release 版本使用不同的签名, 但是却不影响 Debug 和 Custom 的 BuildType.
来源: https://www.cnblogs.com/tangZH/p/10999060.html