背景
- 当你在开发一个app,通常你会有几个版本。大多数情况是你需要一个开发版本,用来测试app和弄清它的质量,然后还需要一个生产版本。这些版本通常有不同的设置,例如不同的URL地址。更可能的是你可能需要一个免费版和收费版本。基于上述情况,你需要处理不同的版本:开发免费版,开发付费版本,生产免费版,生产付费版,而针对不同的版本不同的配置,这极大增加的管理难度。
- Gradle有一些方便的方法来管理这些问题。我们很早之前谈过debug和release版本,现在我们谈到另外一个概念,不同的产品版本。构建版本和生产版本通常可以合并,构建版本和生产版本的合并版叫做构建变种。
构建版本
在Gradle的Android插件中,一个构建版本意味着定义一个app或者依赖库如何被构建,每个构建版本都有特殊的需求,比如是否需要debug,applicationId是什么,是否需要删除无用的资源文件,通过buildTypes方法来定义一个构建版本。
123456789android {buildTypes {release {minifyEnabled falseproguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'}}}这个文件定义了模块的release版本,然后定义了proguard的位置,该release版本不是唯一的构建版本,默认情况下,还有个debug版本,AndroidStudio把它视为默认的构建版本。
创建自己的构建版本
创建构建版本只需要在buildTypes写入自己的版本,如下:
12345678910android {buildTypes {staging {applicationIdSuffix ".staging"versionNameSuffix "-staging"buildConfigField "String", "API_URL","\"http://staging.example.com/api\""}}}上面定义了一个staging版本,该版本定义了一个新的applicationid,这让其与debug和release版本的applicationId不同,假设使用了默认的配置,那么applicationID将会是这样:
- Debug:com.package
- Release: com.package
- Staging: com.package.staging
构建类型默认的属性和值如下图所示:
这意味着你可以在你的设备上安装staging版本和release版本,但是你可以继承已有的版本:
1234567891011android {buildTypes {staging.initWith(buildTypes.debug)staging {applicationIdSuffix ".staging"versionNameSuffix "-staging"debuggable = false}}}// initWith()方法创建了一个新的版本的同时,复制所有存在的构建版本,类似于继承。
Source sets
- 当你创建了一个新的构建版本,Gradle也创建了新的source set。使用source set的目的是将一些源文件组合起来,为了某个特殊目的在逻辑上进行分组,方便对文件进行管理。默认情况下,该文件夹不会自动为你创建,所以要手工创建。12345678910111213141516171819202122232425262728293031323334353637app└── 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类到staging版本,你可以添加相同的类到debug和release版本,但是不能添加到main版本,否则会抛出异常。
当使用不同的source sets时,资源文件的处理需要特殊的方式,Drawable和layout文件将会覆盖在main中的重名文件,但是values文件夹下的资源不会,gradle将会把这些资源连同main里面的资源一起合并。举个例子
12345678910111213141516在main中创建了一个string.xml<resources><string name="app_name">TypesAndFlavors</string><string name="hello_world">Hello world!</string></resources>在staging版本也添加了string.xml<resources><string name="app_name">TypesAndFlavors STAGING</string></resources>然后合并的strings.xml将会是这样的:<resources><string name="app_name">TypesAndFlavors STAGING</string><string name="hello_world">Hello world!</string></resources>当你创建一个新的构建版本不是staging,最终的string.xml将会是main目录下的strings.xmlmanifest和value的情况一样,Android插件会合并他们,比如,你在staging下面创建一个manifest,那么你只需要添加自己的标签,不需要去拷贝在main下面的mainfest文件。
依赖包
- 每一个构建版本都有自己的依赖包,如果你想为debug版本添加一个logging框架,可以这么做:12345dependencies {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有付费和免费版。它简化了基于相同的代码构建不同版本的app。
创建product flavors
productFlavors代码如下:
12345678910111213android {productFlavors {red {applicationId 'com.gradleforandroid.red'versionCode 3}blue {applicationId 'com.gradleforandroid.blue'minSdkVersion 14versionCode 4}}}product flavors和构建版本的配置不同,因为product flavors有自己的ProductFlavor类,就像defaultConfig,这意味着你的所有的productFlavors都分享一样的属性。
多个flavor构建变体
在某些场景下,你可能需要创建一些product flavors的合并版本,举个例子:client A 和client B都需要一个free和paid版本,而他们又都基于一样的代码,但是不一样的颜色,如果创建4个不同的flavors意味着重复的配置,合并flavors最简单的做法可能是使用flavor dimensions:
1234567891011121314151617android {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
构建变体
- 构建变体是构建版本和生产版本的结合体,可以在AndroidStudio左下角的BuildVariants中查看。
- 如果没有product flavors,那么变体就只包含构建版本,即使没有定义任何构建版本,AndroidStudio 也会默认为你创建debug版本的。
tasks
- Android插件会为每一个变体创建不同的配置,一个新的Android项目会有debug和release版本,所以可以使用assembleDebug和assembleRelease,也可以只使用assemble,debug和release都会执行,当添加了新的构建版本或者生产版本,同样的也会创建新的task。以上面的productFlavors为例:
- 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中的代码和构建的代码,当然,依赖项目也可以提供额外的资源,他们也会被合并,举个例子:你不想在main中申明这个权限,因为可能会导致一些问题,所以你可以添加一个额外的manifest文件在debug的文件夹中,申请额外的权限。资源和manifest的优先级是这样的:
- 如果一个资源在main中和在flavor中定义了,那么那个在flavor中的资源有更高的优先级。这样那个在flavor文件夹中的资源将会被打包到apk。而在依赖项目申明的资源总是拥有最低优先级。
创建构建变体
- gradle让处理构建变体变得更加容易:123456789101112131415161718192021222324android {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"}}}
变体过滤器
- 忽略某个变体是可行的,这样你可以加速你的构建,当使用assemble时候,将不会执行你不需要的变体,在build.gradle中添加代码:12345678android.variantFilter { variant ->if(variant.buildType.name.equals('release')) {variant.getFlavors().each() { flavor ->if (flavor.name.equals('blue')) { variant.setIgnore(true);}}}}
签名配置
在发布你的应用之前,你需要为你的app私钥签名,如果你有付费版和免费版,你需要有不同的key去签名不同的变体,配置签名可以这样定义:
1234567891011android {signingConfigs {staging.initWith(signingConfigs.debug)release {storeFile file("release.keystore")storePassword"secretpassword"keyAlias "gradleforandroid"keyPassword "secretpassword"}}}上面这个例子,我们创建了2个不同的签名配置,debug配置是AndroidStudio默认的,其使用了公共的keystore和password。release配置默认使用了storeFile,定义了key alias和password。
在构建版本有一个属性叫做singingConfig,可以这么配置:
1234567android {buildTypes {release {signingConfig signingConfigs.release}}}假如你需要对每个版本生成不同的验证,你可以这么定义:
1234567android {productFlavors {blue {signingConfig signingConfigs.release}}}但是上面也提到,buildType的优先级高于flavor,所以在flavor中定义可能会被覆盖,最好的方式如下:
12345678android {buildTypes {release {productFlavors.red.signingConfig signingConfigs.redproductFlavors.blue.signingConfig signingConfigs.blue}}}