Gradle for Android ( 构建变体 )
有时候我们一个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
source set
。默认的source set目录会放在相同的build type的目录下。当你创建一个新的build type时,该目录不会自动创建,你必须在你使用代码与资源前自己为每一个build type创建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 ├── 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
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。
推荐阅读
-
Android Studio手动配置Gradle的方法
-
Android Studio Gradle依赖冲突解决方法
-
史上最全Android build.gradle配置详解(小结)
-
将Android封装库通过gradle部署到maven私服并依赖使用
-
Gradle-构建脚本
-
详解Android Gradle插件3.0挖坑日记
-
AndroidStudio修改默认C盘配置文件夹(.android.gradle.AndroidStudio)以及修改后避免踩的坑
-
浅析Android中build.gradle的实用技巧
-
Android Studio遇到Failed to sync Gradle project错误时的解决办法
-
Android 模块构建错误不能下载依赖包