注册 登录
  • 欢迎访问Sharezer Blog

Gradle for Android 第四篇( 构建变体 )

Gradle sharezer 552次浏览 0个评论

原文地址 https://segmentfault.com/a/1190000004241503

这是一个系列,所以如果你看完这篇文章,请看下列文章:

Gradle for Android 第一篇 (从 Gradle 和 AS 开始)
Gradle for Android 第二篇 (Build.gradle 入门)
Gradle for Android 第三篇 (依赖管理)
Gradle for Android 第四篇 (构建变体)
Gradle for Android 第五篇 (多模块构建)
Gradle for Android 第六篇 (测试)
Gradle for Android 第七篇 (Groovy 入门)

当你在开发一个 app, 通常你会有几个版本。大多数情况是你需要一个开发版本,用来测试 app 和弄清它的质量,然后还需要一个生产版本。这些版本通常有不同的设置,例如不同的 URL 地址。更可能的是你可能需要一个免费版和收费版本。基于上述情况,你需要处理不同的版本:开发免费版,开发付费版本,生产免费版,生产付费版,而针对不同的版本不同的配置,这极大增加的管理难度。

Gradle 有一些方便的方法来管理这些问题。我们很早之前谈过 debug 和 release 版本,现在我们谈到另外一个概念,不同的产品版本。构建版本和生产版本通常可以合并,构建版本和生产版本的合并版叫做构建变种。

这一章我们将学习构建版本,它能使得开发更有效率,并且学习如何使用它们。然后我们会讨论构建版本和生产版本的不同,以及如何将其合并。我们会探讨签名机制,如何针对不同的变种签名等。

在这一章,我们遵循如下规则:

  • Build types

  • Product flavors

  • Build variants

  • Signing configurations

构建版本

在 Gradle 的 Android 插件中,一个构建版本意味着定义一个 app 或者依赖库如何被构建。每个构建版本都要特殊的一面,比如是否需要 debug,application id 是什么,是否不需要的资源被删除等等。你可以定义一个构建版本通过 buildTypes 方法。例如:

android {
       buildTypes {
           release {
               minifyEnabled false
               proguardFiles getDefaultProguardFile
                 ('proguard-android.txt'), 'proguard-rules.pro'
           }
        }
 }

这个文件定义了该模块是 release 版本,然后定义了 proguard 的位置。该 release 版本不是唯一的构建版本,默认情况下,还有个 debug 版本。Android studio 把它视为默认构建版本。

创建自己的构建版本

当默认的构建版本不够用的时候,创建版本也是很容易的一件事,创建构建版本你只需要在 buildTypes 写入自己的版本。如下所示:

android {
    buildTypes {
        staging {
            applicationIdSuffix ".staging"
            versionNameSuffix "-staging"
            buildConfigField "String", "API_URL",
            "\"http://staging.example.com/api\""
         }
    }
}

我们定义了一个 staging 版本,该版本定义了一个新的 application id,这让其与 debug 和 release 版本的 applicationID 不同。假设你使用了默认的配置,那么 applicationID 将会是这样的:

  • Debug: com.package

  • Release: com.package

  • Staging: com.package.staging

这意味着你可以在你的设备上安装 staging 版本和 release 版本。staging 版本也有自己的版本号。buildConfigField 定义了一个新的 URL 地址。你不必事事都去创建,所以最可能的方式是去继承已有的版本。

android {
       buildTypes {
           staging.initWith(buildTypes.debug)
           staging {
               applicationIdSuffix ".staging"
               versionNameSuffix "-staging"
               debuggable = false
           } 
        }
}

initWith() 方法创建了一个新的版本的同时,复制所有存在的构建版本,类似继承。我们也可以复写该存在版本的所有属性。

Source sets

当你创建了一个新的构建版本,Gradle 也创建了新的 source set。默认情况下,该文件夹不会自动为你创建,所有你需要手工创建。

app
└── src
├── debug
│ ├── java
       │   │   └── com.package
 │ │
│ ├── res
│ │ └── layout
│   │       └── activity_main.xml
│   └── AndroidManifest.xml
├── main
│ ├── java
│   │   └── com.package
│ │
│ ├── res
└── MainActivity.java
└── Constants.java
│ │
│ │
│ │
│   └── AndroidManifest.xml
├── staging
│ ├── java
│   │   └── com.package
├── drawable
└── layout
└── activity_main.xml
│ │
│ ├── res
│ │ └── layout
│   │       └── activity_main.xml
│   └── AndroidManifest.xml
└── release
    ├── java
    │   └── com.package
    │       └── Constants.java
    └── AndroidManifest.xml

注意:当你添加一个 Java 类的时候,你需要知道以下过程,当你添加了 CustomLogic.java 到 staging 版本,你可以添加相同的类到 debug 和 release 版本,但是不能添加到 main 版本。如果你添加了,会抛出异常。

当使用不同的 source sets 的时候,资源文件的处理需要特殊的方式。Drawables 和 layout 文件将会复写在 main 中的重名文件,但是 values 文件下的资源不会。gradle 将会把这些资源连同 main 里面的资源一起合并。

举个例子,当你在 main 中创建了一个 srings.xml 的时候:

<resources>
       <string >TypesAndFlavors</string>
       <string >Hello world!</string>
</resources>

当你在你的 staing 版本也添加了 rings.xml:

<resources>
       <string >TypesAndFlavors STAGING</string>
</resources>

然后合并的 strings.xml 将会是这样的:

<resources>
       <string >TypesAndFlavors STAGING</string>
       <string >Hello world!</string>
</resources>

当你创建一个新的构建版本而不是 staging,最终的 strings.xml 将会是 main 目录下的 strings.xml。

manifest 也和 value 文件下的文件一样。如果你为你的构建版本创建了一个 manifest 文件,那么你不必要去拷贝在 main 文件下的 manifest 文件,你需要做的是添加标签。Android 插件将会为你合并它们。

我们将在会之后的章节讲到合并的更多细节。

依赖包

每一个构建版本都有自己的依赖包,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 创建不同版本。典型的例子是,一个 app 有付费和免费版。product flavors 极大简化了基于相同的代码构建不同版本的 app。

如果你不确定你是否需要一个新的构建版本或者 product flavors,你应该问你自己,你是否需要内部使用和外部使用的 apk。如果你需要一个完全新的 app 去发布,和之前的版本完全隔离开,那么你需要 product flavors。否则你只是需要构建版本。

创建 product flavors

创建 product flavors 非常的容易。你可以在 productFlavors 中添加代码:

android {
    productFlavors {
        red {
             applicationId 'com.gradleforandroid.red'
             versionCode 3
        }
        blue {
             applicationId 'com.gradleforandroid.blue'
             minSdkVersion 14
             versionCode 4
        }
    }
}

product flavors 和构建版本的配置不同。因为 product flavors 有自己的 ProductFlavor 类,就像 defaultConfig,这意味着你的所有 productFlavors 都分享一样的属性。

Source sets

就像构建版本一样,product Flavors 也有自己的代码文件夹。创建一个特殊的版本就像创建一个文件夹那么简单。举个例子,当你有的生产版本的 blue flavors 有一个不同的 app 图标,该文件夹需要被叫做 blueRelease。

多个 flavors 构建变体

在一些例子中,你可能需要创建一些 product flavors 的合并版本。举个例子,client A 和 client B 可能都想要一个 free 和 paid 的版本,而他们又都是基于一样的代码,但是有不一样的颜色等。创建四个不同的 flavors 意味着有重复的配置。合并 flavors 最简单的做法可能是使用 flavor dimensions,就像这样:

android {
       flavorDimensions "color", "price"
       productFlavors {
           red {
               flavorDimension "color"
           }
           blue {
               flavorDimension "color"
           }
           free {
               flavorDimension "price"
           }
           paid {
               flavorDimension "price"
           }
       }
}

当你添加了 flavor dimensions,你就需要为每个 flavor 添加 flavorDimension,否则会提示错误。flavorDimensions 定义了不同的 dimensions,当然其顺序也很重要。当你合并二个不同的 flavors 时,他们可能有一样的配置和资源。例如上例:

  • blueFreeDebug and blueFreeRelease

  • bluePaidDebug and bluePaidRelease

  • redFreeDebug and redFreeRelease

  • redPaidDebug and redPaidRelease

构建变体

构建变体是构建版本和生产版本的结合体。当你创建了一个构建版本或者生产版本,同样的,新的变体也会被创建。举个例子,当你有 debug 和 release 版本,你创建了 red 和 blue 的生产版本,那么变体将会有四个:

Gradle for Android 第四篇( 构建变体 )

你可以在 Android studio 的左下角找到它,或者通过 VIEW|Tool Windows|Build Variants 打开它。该视图列出了所有的变体,并且允许你去切换它们。改变他们将会影响到你按 Run 按钮。

如果你没有 product flavors,那么变体只是简单的包含构建版本,就算你没有定义任何构建版本,Android studio 也会默认为你创建 debug 版本的。

tasks

android 插件回味每一个变体创建不同的配置。一个新的 Android 项目会有 debug 和 release 版本,所有你可以使用 assembleDebug 和 assembleRelease,当然当你使用 assemble 命令,会二者都执行。当你添加了一个新的构建版本,新的 task 也会被创建。例如:

  • assembleBlue uses the blue flavor configuration and assembles both BlueRelease and BlueDebug.

  • assembleBlueDebug combines the flavor configuration with the build type configuration, and the flavor settings override the build type settings.

Source sets

构建变体也可以有自己的资源文件夹,举个例子,你可以有 src/blueFreeDebug/java/。

资源文件和 manifest 的合并

在打包 app 之前,Android 插件会合并 main 中的代码和构建的代码。当然,依赖项目也可以提供额外的资源,它们也会被合并。你可能需要额外的 Android 权限针对 debug 变体。举个例子,你不想在 main 中申明这个权限,因为这可能导致一些问题,所以你可以添加一个额外的 mainfest 文件在 debug 的文件夹中,申明额外的权限。

资源和 mainfests 的优先级是这样的:

Gradle for Android 第四篇( 构建变体 )

如果一个资源在 main 中和在 flavor 中定义了,那么那个在 flavor 中的资源有更高的优先级。这样那个在 flavor 文件夹中的资源将会被打包到 apk。而在依赖项目申明的资源总是拥有最低优先级。

创建构建变体

gradle 让处理构建变体变得容易。

android {
       buildTypes {
           debug {
               buildConfigField "String", "API_URL",
               "\"http://test.example.com/api\""
        }
           staging.initWith(android.buildTypes.debug)
           staging {
               buildConfigField "String", "API_URL",
                 "\"http://staging.example.com/api\""
               applicationIdSuffix ".staging"
           }
       }
       productFlavors {
           red {
               applicationId "com.gradleforandroid.red"
               resValue "color", "flavor_color", "#ff0000"
           }
           blue {
               applicationId "com.gradleforandroid.blue"
               resValue "color", "flavor_color", "#0000ff"
           } 
     }
}

在这个例子中,我们创建了 4 个变体,分别是 blueDebug,blueStaging,redDebug,redStaging。每一个变体都有其不同的 api url 以及颜色。例如:

Gradle for Android 第四篇( 构建变体 )

Gradle for Android 第四篇( 构建变体 )

变体过滤器

忽略某个变体也是可行的。这样你可以加速你的构建当使用 assemble 的时候,这样你列出的 tasks 将不会执行那么你不需要的变体。你可以使用过滤器,在 build.gradle 中添加代码如下所示:

if(variant.buildType.name.equals('release')) { variant.getFlavors().each() { flavor -> if (flavor.name.equals('blue')) { variant.setIgnore(true); } } } } " title="" data-original-title="复制">

android.variantFilter { variant ->
       if(variant.buildType.name.equals('release')) {
           variant.getFlavors().each() { flavor ->
               if (flavor.name.equals('blue')) { variant.setIgnore(true);
            }
        }
    }
}

在这个例子中,我们检查下:

Gradle for Android 第四篇( 构建变体 )

你可以看到 blueFreeRelease 和 bluePaidRelease 被排除在外,如果你运行 gradlew tasks,你会发现所有的关于上述变体的 tasks 不再存在。

签名配置

在你发布你的应用之前,你需要为你的 app 私钥签名。如果你有付费版和免费版,你需要有不同的 key 去签名不同的变体。这就是配置签名的好处。配置签名可以这样定义:

android {
       signingConfigs {
           staging.initWith(signingConfigs.debug)
           release {
               storeFile file("release.keystore")
               storePassword"secretpassword"
               keyAlias "gradleforandroid"
               keyPassword "secretpassword"
           }
      }
}

在这个例子中,我们创建了 2 个不同的签名配置。debug 配置是 as 默认的,其使用了公共的 keystore 和 password,所以没有必要为 debug 版本创建签名配置了。staging 配置使用了 initWith() 方法,其会复制其他的签名配置。这意味着 staging 和 debug 的 key 是一样的。

release 配置使用了 storeFile,定义了 key alias 和密码。当然这不是一个好的选择,你需要在 Gradle properties 文件中配置。

当你定义了签名配置后,你需要应用它们。构建版本都有一个属性叫做 signingConfig,你可以这么干:

android {
       buildTypes {
           release {
               signingConfig signingConfigs.release
           } 
       }
}

上例使用了 buildTypes,但是你可能需要对每个版本生成不同的验证,你可以这么定义:

android {
       productFlavors {
           blue {
               signingConfig signingConfigs.release
           }
       }
}

当然,你在 flavor 中定义这些,最好会被重写,所以最好的做法是:

android {
       buildTypes {
           release {
               productFlavors.red.signingConfig signingConfigs.red
               productFlavors.blue.signingConfig signingConfigs.blue
           }
       }
}

总结

在这一章,我们讨论了构建版本和生产版本,以及如何结合它们。这将会是非常有用的工具,当你需要不同的 url 以及不同的 keys,而你们有相同的代码和资源文件,但是有不同的类型以及版本,构建版本和生产版本将会让你的生活更美好。

我们也谈论了签名配置以及如何使用他们。

下一章,你将会学习到多模块构建,因为当你想把你的代码分成一个依赖包或者依赖项目的时候,或者你想把 Android wear 模块放在你的应用的时候,这将非常重要。


Sharezer , 版权所有丨如未注明 , 均为原创丨本网站采用BY-NC-SA协议进行授权 , 转载请注明Gradle for Android 第四篇( 构建变体 )
喜欢 (0)
[liangshaoze@sina.cn]
分享 (0)
发表我的评论
取消评论

表情 贴图 加粗 删除线 居中 斜体 签到

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址