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

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

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

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

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

  • Build types
  • Product flavors
  • Build variants
  • Signing configurations

构建版本

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


  1. android { 
  2.        buildTypes { 
  3.            release { 
  4.                minifyEnabled false 
  5.                proguardFiles getDefaultProguardFile 
  6.                  ('proguard-android.txt'), 'proguard-rules.pro' 
  7.            } 
  8.         } 
  9.  }  

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

创建自己的构建版本

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


  1. android { 
  2.     buildTypes { 
  3.         staging { 
  4.             applicationIdSuffix ".staging" 
  5.             versionNameSuffix "-staging" 
  6.             buildConfigField "String", "API_URL", 
  7.             "\"http://staging.example.com/api\"" 
  8.          } 
  9.     } 
  10. }  

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

  • Debug: com.package
  • Release: com.package
  • Staging: com.package.staging

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


  1. android { 
  2.        buildTypes { 
  3.            staging.initWith(buildTypes.debug) 
  4.            staging { 
  5.                applicationIdSuffix ".staging" 
  6.                versionNameSuffix "-staging" 
  7.                debuggable = false 
  8.            }  
  9.         } 
  10. }  

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

Source sets

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


  1. app 
  2. └── src 
  3. ├── debug 
  4. │ ├── java 
  5.        │   │   └── com.package 
  6.  │ │ 
  7. │ ├── res 
  8. │ │ └── layout 
  9. │   │       └── activity_main.xml 
  10. │   └── AndroidManifest.xml 
  11. ├── main 
  12. │ ├── java 
  13. │   │   └── com.package 
  14. │ │ 
  15. │ ├── res 
  16. └── MainActivity.java 
  17. └── Constants.java 
  18. │ │ 
  19. │ │ 
  20. │ │ 
  21. │   └── AndroidManifest.xml 
  22. ├── staging 
  23. │ ├── java 
  24. │   │   └── com.package 
  25. ├── drawable 
  26. └── layout 
  27. └── activity_main.xml 
  28. │ │ 
  29. │ ├── res 
  30. │ │ └── layout 
  31. │   │       └── activity_main.xml 
  32. │   └── AndroidManifest.xml 
  33. └── release 
  34.     ├── java 
  35.     │   └── com.package 
  36.     │       └── Constants.java 
  37.     └── AndroidManifest.xml  

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

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

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


  1. <resources> 
  2.        <string name="app_name">TypesAndFlavors</string> 
  3.        <string name="hello_world">Hello world!</string> 
  4. </resources>  

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


  1. <resources> 
  2.        <string name="app_name">TypesAndFlavors STAGING</string> 
  3. </resources>  

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


  1. <resources> 
  2.        <string name="app_name">TypesAndFlavors STAGING</string> 
  3.        <string name="hello_world">Hello world!</string> 
  4. </resources>  

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

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

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

依赖包

每一个构建版本都有自己的依赖包,gradle自动为每一个构建的版本创建不同的依赖配置。如果你想为debug版本添加一个logging框架,你可以这么做:


  1. dependencies { 
  2.  
  3.    compile fileTree(dir: 'libs', include: ['*.jar']) 
  4.    compile 'com.android.support:appcompat-v7:22.2.0' 
  5.    debugCompile 'de.mindpipe.android:android-logging-log4j:1.0.3' 
  6. }  

你可以结合不同的构建版本着不同的构建配置,就像这种方式,这让你的不同版本的不同依赖包成为可能。

product flavors

和构建版本不同,product flavors用来为一个app创建不同版本。典型的例子是,一个app有付费和免费版。product flavors极大简化了基于相同的代码构建不同版本的app。

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

创建product flavors

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


  1. android { 
  2.     productFlavors { 
  3.         red { 
  4.              applicationId 'com.gradleforandroid.red' 
  5.              versionCode 3 
  6.         } 
  7.         blue { 
  8.              applicationId 'com.gradleforandroid.blue' 
  9.              minSdkVersion 14 
  10.              versionCode 4 
  11.         } 
  12.     } 
  13. }  

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,就像这样:


  1.  android { 
  2.        flavorDimensions "color", "price" 
  3.        productFlavors { 
  4.            red { 
  5.                flavorDimension "color" 
  6.            } 
  7.            blue { 
  8.                flavorDimension "color" 
  9.            } 
  10.            free { 
  11.                flavorDimension "price" 
  12.            } 
  13.            paid { 
  14.                flavorDimension "price" 
  15.            } 
  16.        } 

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

  • blueFreeDebug and blueFreeRelease
  • bluePaidDebug and bluePaidRelease
  • redFreeDebug and redFreeRelease
  • redPaidDebug and redPaidRelease

构建变体

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

你可以在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的优先级是这样的:

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

创建构建变体

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


  1. android { 
  2.        buildTypes { 
  3.            debug { 
  4.                buildConfigField "String", "API_URL", 
  5.                "\"http://test.example.com/api\"" 
  6.         } 
  7.            staging.initWith(android.buildTypes.debug) 
  8.            staging { 
  9.                buildConfigField "String", "API_URL", 
  10.                  "\"http://staging.example.com/api\"" 
  11.                applicationIdSuffix ".staging" 
  12.            } 
  13.        } 
  14.        productFlavors { 
  15.            red { 
  16.                applicationId "com.gradleforandroid.red" 
  17.                resValue "color", "flavor_color", "#ff0000" 
  18.            } 
  19.            blue { 
  20.                applicationId "com.gradleforandroid.blue" 
  21.                resValue "color", "flavor_color", "#0000ff" 
  22.            }  
  23.      } 
  24. }  

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

变体过滤器

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


  1. android.variantFilter { variant -> 
  2.        if(variant.buildType.name.equals('release')) { 
  3.            variant.getFlavors().each() { flavor -> 
  4.                if (flavor.name.equals('blue')) { variant.setIgnore(true); 
  5.             } 
  6.         } 
  7.     } 
  8. }  

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

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

签名配置

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


  1.  android { 
  2.        signingConfigs { 
  3.            staging.initWith(signingConfigs.debug) 
  4.            release { 
  5.                storeFile file("release.keystore") 
  6.                storePassword"secretpassword" 
  7.                keyAlias "gradleforandroid" 
  8.                keyPassword "secretpassword" 
  9.            } 
  10.       } 
  11. }  

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

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

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


  1. android { 
  2.        buildTypes { 
  3.            release { 
  4.                signingConfig signingConfigs.release 
  5.            }  
  6.        } 
  7. }  

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


  1. android { 
  2.        productFlavors { 
  3.            blue { 
  4.                signingConfig signingConfigs.release 
  5.            } 
  6.        } 
  7. }  

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


  1. android { 
  2.        buildTypes { 
  3.            release { 
  4.                productFlavors.red.signingConfig signingConfigs.red 
  5.                productFlavors.blue.signingConfig signingConfigs.blue 
  6.            } 
  7.        } 
  8. }  

总结

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

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

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

本文作者:佚名

来源:51CTO

时间: 2024-09-20 06:43:32

Gradle for Android 第四篇( 构建变体 )的相关文章

Gradle for Android 第三篇( 依赖管理 )

Gradle for Android 第三篇( 依赖管理 ) 依赖管理是Gradle最闪耀的地方,最好的情景是,你仅仅只需添加一行代码在你的build文件,Gradle会自动从远程仓库为你下载相关的jar包,并且保证你能够正确使用它们.Gradle甚至可以为你做的更多,包括当你在你的工程里添加了多个相同的依赖,gradle会为你排除掉相同的jar包. 作者:佚名来源:Android开发中文站|2017-04-10 17:35  移动端  收藏   分享 依赖管理 依赖管理是Gradle最闪耀的地

产品变体设计---考虑事项、动机和最佳实践

简介 为了解何时以及如何设计产品变体,应注意观察产品之间的差异.举例来说,两种不同的卡车型号可能存在十几种或者更多的不同特性. 通常情况下,有时由于企业对新产品的市场经验不足,企业最初并没有设计变体.产品开发团队在最初开发产品时,往往以特定客户或用例为目标.随着逐渐开始收集到关于产品的客户反馈,对初始产品的多种变体的需要也开始出现.如果产品变体未能得到有效开发,那么由于存在重复性的工作,同时维护和改进多个产品版本的任务将会变得低效而又耗时. 与从最初起便考虑到产品变体相比,跨多种产品单独维护类似

Gradle for Android第一篇( 从Gradle和AS开始 )

正如大家所见,这是本英文书,而由于国内的gradle翻译资料不全,所以特次开辟专栏,翻译gradle for android这本书,同时添加自己的心得体会以及在实际工作上的实战,希望大家能够喜欢. 如果你是名Android开发新手,或者是名从eclipse切换到Android studio的新手,那么我强烈建议您follow我的文章,正如封面所见,利用gradle构建工具来自动构建你的Android项目.废话不多说,我们直接开始吧. 今天主要介绍Android studio工具的使用,以及cra

Gradle for Android系列:为什么Gradle这么火

Android Studio 占领市场后,构建工具 Gradle 的地位无人能比,我们有必要学习.使用它来为我们创造价值. 在深入学习 Gradle 之前,我们有必要了解下它为什么这么流行. Android 应用的构建过程 Android 应用程序的构建过程非常复杂,如图所示: 主要有以下几步: 主要的资源文件(layout, values 等)都被 aapt 编译,并且在一个 R 文件中引用 Java 代码被 Java 编译器编译成 JVM 字节码(.class 文件) JVM 字节码再被 d

android控件点击变背景

问题描述 android控件点击变背景 怎么在点击一个控件后改变背景呢?我说的是在不点击其他控件的时候保持背景的改变,不是点击的瞬间改变,再强调一句:不是点击的瞬间改变 解决方案 Android控件背景点击改变 解决方案二: 如果没理解错你的意思的话, 事件监听+延迟 解决方案三: 监听click事件,然后调用postDelayed 解决方案四: 用选择器selector呀,设置一下就行了 解决方案五: 开启线程延迟执行切换背景 解决方案六: 表达不清楚~~~~~

微服务的持续集成,四步“构建”一个代码世界

本文讲的是微服务的持续集成,四步"构建"一个代码世界,大师Martin Fowler对持续集成是这样定义的:持续集成是一种软件开发实践,即团队开发成员经常集成他们的工作,通常每个成员每天至少集成一次,也就意味着每天可能会发生多次集成.每次集成都通过自动化的构建(包括编译,发布,自动化测试)来验证,从而尽快地发现集成错误. 今天我们就来聊一聊微服务的持续集成. 目录 一.持续集成之构建 二.持续集成之部署 三.持续集成之测试 四.持续集成之发布 五.总结 一.持续集成之构建 当微服务产生

gradle打包android程序时,如何修改java文件中的属性值

问题描述 gradle打包android程序时,如何修改java文件中的属性值 工程有个文件例如1.java文件:其中有个public final static String URL = "/www.baidu.com"; 现在的问题是如何在用gradle打包的时候可以更换1.java文件中的URL? ant打包就可以写不同的build.xml,在build.xml中更换URL的字符串,在利用ant构建的时候通过编译不同的build.xml来达到更换URL的目的. 现在不知道gradl

android开发环境的构建

问题描述 android开发环境的构建 感觉少了很多东西,有什么方法增加吗? 解决方案 这张图里面少了很东西,有办法增加吗? 解决方案二: 构建android开发环境......答案就在这里:第二课构建android开发环境 解决方案三: 你是不是没有VPN呀?而且你改用Android Studio了 解决方案四: 你把show后面那两个复选框别选中

如何高效运作机器学习团队(机器学习入门第四篇)

本文是机器学习入门教程的第四篇,前三篇分别是: 1.机器学习能为你的业务做什么?有些事情你肯定猜不到 2.关于机器学习算法 你需要了解的东西 3.如何开发机器学习模型? 我们已经讨论了开发机器学习模型的整个过程,现在我们来谈谈谁来实现这个过程,以及团队是如何运作的. 一个欢乐的大家庭 一个"传统"的产品团队由设计师.工程师和产品经理组成,而数据分析师有时也会包含在其中,但大多数情况下是多个团队共享这个稀缺的资源.在数据科学成为公司DNA一部分的时候,数据科学家必须要成为产品团队的一个重