文档章节

Gradle for Android ( 构建变体 )

SEOwhywhy
 SEOwhywhy
发布于 2019/06/21 17:55
字数 2651
阅读 26
收藏 0

  有时候我们一个app需要有不同的版本,不同的版本又会使用不同的配置,我们可以使用gradle进行管理。
  
  Build types
  
  Product flavors
  
  Build variants
  
  Signing configurations
  
  一、构建版本Build types:
  
  常见的构建版本有debug与release。
  
  复制代码
  
  buildTypes {
  
  release {
  
  minifyEnabled false
  
  proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
  
  }
  
  }
  
  复制代码
  
  自定义构建版本:
  
  复制代码
  
  custom {
  
  applicationIdSuffix ".custom"
  
  versionNameSuffix ".custom"
  
  minifyEnabled false
  
  proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
  
  signingConfig signingConfigs.readerDaquanConfig
  
  }
  
  复制代码
  
  除了debug构建版本不需要签名外,其它的都是需要配置签名的,不然无法运行在手机上,该版本定义了新的applicationId与版本号。不同构建版本的applicationId如下:
  
  Debug: com.package
  
  Release: com.package
  
  Staging: com.package.staging
  
  也可以采用继承的方式:
  
  复制代码
  
  custom.initWith(buildTypes.debug)
  
  custom {
  
  applicationIdSuffix ".custom"
  
  versionNameSuffix ".custom"
  
  minifyEnabled false
  
  proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
  
  signingConfig signingConfigs.readerDaquanConfig
  
  }
  
  复制代码
  
  custom继承debug构建版本的配置,custom中的配置会覆盖debug的配置。
  
  在BuildConfig中添加变量
  
  复制代码
  
  custom {
  
  applicationIdSuffix ".custom"
  
  versionNameSuffix ".custom"
  
  buildConfigField("String", "name","\"custom app\"")
  
  buildConfigField("int", "id", "0")
  
  minifyEnabled false
  
  proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
  
  signingConfig signingConfigs.readerDaquanConfig
  
  }
  
  复制代码
  
  之后我们可以看到:
  
  注意如果要添加的是String,那么双一号里面需要再一个双引号标识字符串"\"custom app\"",斜杆是对里面双引号进行转义。
  
  Source sets
  
  当创建了一个新的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.www.rmutk.net 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"www.xcdeyiju.com>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: ['www.osgjyl.com*.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 www.oushengguoj.com "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 www.xgjrfwsc.cn "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 www.yunyouuyl.com"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 www.yuchengyuLe.com"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。

© 著作权归作者所有

SEOwhywhy
粉丝 9
博文 157
码字总数 346595
作品 0
私信 提问
Android Studio 升级到3.0后的gradle迁移(bing译文)

首先推荐按照官方版进行,如有疑问,评论中讨论; 官方步骤Migrate to Android Plugin for Gradle 3.0.0 英文水平高的可直接点击链接查看原文,以防翻译出错; Gradle 3.0.0 的 Android 插件...

我家有宝
2017/10/27
3.4K
0
Android Studio 3.5 Canary 12 发布

Android Studio 3.5 Canary 12 发布了,Canary 和 Dev 可用。 此版本包括针对以下问题的修复: Android Studio UI Issue #111960180:Android Studio 挂在 UI 线程中(加载 Android SDK 时)...

xplanet
2019/04/21
1K
0
【译】Gradle 的依赖关系处理不当,可能导致你编译异常

文章 | Ashesh Bharadwaj 翻译 | 承香墨影 授权 承香墨影 翻译、编辑并发布 在 Android Studio 中,Gradle 构建过程对于开发者来说,很大程度上是抽象的。作为一个新的 Android 开发者,我们...

承香墨影
2018/10/29
0
0
Android Gradle 隐形依赖的奇怪案例

相信 Android 开发者都有在 Android Studio 中升级 compileSdkVersion 的经历,这个时候如果你使用了 support 包,并同时升级,那么可能会出现一个错误提示。本文教你如何解决这个问题。 在 ...

极光推送
2018/04/16
178
0
关于Android 设置应用 ID applicationId

每个 Android 应用均有一个唯一的应用 ID,像 Java 软件包名称一样,如 com.example.myapp。 此 ID 可以在设备上和 Google Play 商店中对您的应用进行唯一标识。 如果您想要上传新版本的应用...

临江仙卜算子
2018/07/05
34
0

没有更多内容

加载失败,请刷新页面

加载更多

How to find table in a database with HeidiSQL

In this article I want to show you how you can find table by name with HeidiSQL. Find table by typing One of the options to find table is to having focus in the object explorer ......

Ciet
11分钟前
4
0
基于SWIG跨平台开发的C++编码规范

1、数组定义 使用数组不建议采用指针方式eg double *,或者 double test[4] 直接采用std::vector或list即可。对于固定长度的数组定义为一个结构体 double test[4]instead ofstruct Vec...

洋碱
13分钟前
2
0
用Markdown编程之布局

基本就是用Markdown的布局方式。 \:是转义符号,最高优先级。 行首+# :用于空间布局,1-6分别标明:模式根、子模式、子模式内。 行首+> :用于标注和通信,1个标明标注,2个标明分类,3个标...

dwcz
20分钟前
3
0
SpringBoot定时器多线程解决方案

@Scheduled 作用:spring定时器(定时执行一次或定时轮询执行一段代码) 使用场景:注解在方法上 参数说明:常用参数 @Scheduled 参数说明 String cron:cron表达式定义了方法执行的时间规则(网...

whoisliang
20分钟前
4
0
3.01、Spring AOP的理解

注:转 https://mp.weixin.qq.com/s/PsgTLn8cdTxdd542XgVkUA 什么是AOP AOP(Aspect-Oriented Programming), 即 面向切面编程 , 它与 OOP( Object-Oriented Programming, 面向对象编程) 相辅相......

追忆2025
26分钟前
3
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部