文档章节

Android 自定义构建类型 BuildType

猴亮屏
 猴亮屏
发布于 2018/10/19 09:47
字数 2221
阅读 12
收藏 0

最近接触到自定义构建类型 BuildType,发现这一块有些地方稍不注意的话会被绕进去浪费点时间,既然我这边已经花费时间了,如果正好你也需要接触到 BuildType,也许接下来分享的 tips 可能会帮你节省些时间。

缘起

BuildType 相信许多开发者都不陌生,很常见的一种使用场景是线上、线下的后台接口 BaseUrl 不同,许多人会选择在 build.gradle 文件的 buildTypes 中定义全局变量来实现线上线下环境的定义(Gradle 2.x 版本),例如:

buildTypes {
    debug {
        buildConfigField "String", "BASE_URL", "\"http://debug.api/\""
    }
    release {
        buildConfigField "String", "BASE_URL", "\"https://release.api/\""        
    }
}
1
2
3
4
5
6
7
8
在开发过程中,除了默认的 Debug 和 Release 版本,我们可能还需要为程序自定义一些东西。比如在上线 release 版本前,还需要一个预发布版本,该版本除了后台接口的 BaseUrl 与线上版本不同外,其他资源(包括数据库环境)都与线上相同,该版本用来做发布前的最后测试,最大程度避免线上环境出问题。如果每次打预发版本都去直接修改代码中的 BaseUrl 很明显不是最优解。有一种解决方案是自定义 BuildType,在 app 模块下的 build.gradle 的 buildTypes 中自定义新的构建版本:

buildTypes {
    debug {
        buildConfigField "String", "BASE_URL", "\"http://debug.api/\""
    }

    release {
        buildConfigField "String", "BASE_URL", "\"https://release.api/\""        
    }

    pre.initWith(release) 
    pre {
        buildConfigField "String", "BASE_URL", "\"https://pre.api/\""  
    }
}

//java 类中调用 BuildConfig.BASE_URL 获取定义的变量
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
initWith() 是 BuildType 的一项配置项,我们还可以看到上文中提到的buildConfigField其实也是一项配置项。该配置可以理解成initWith(release) 可以理解成拷贝了 release 这一构建类型的所有变量,因为我们知道,每一个构建类型都有一些默认的变量,例如debuggable、zipAlignEnabled等,使用该配置就免去为新增的构建类型定义所有的变量。 
定义了新的构建类型后,gradle会自动生成新的task,使用gradle assemblePre即可打包新定义的预发包。这里需要稍微注意的地方就是,必须在 app 模块下的 build.gradle 中定义新的构建类型,gradle 才会生成新的task。

Moduel 中自定义 BuildType 的问题

随着现在模块化开发越来越流行,许多项目都会将一些业务无关的模块独立出去,作为 Moduel 在项目中依赖使用,以此达到复用的效果。很常见的例如网络库可能就会被独立出来,那么上文中的关于就会被定义在子 Module 中。这里不注意的话就会浪费一些时间。假设你在 app 模块与子模块的 build.gradle 的 buildTypes 中都如上文定义了三种类型 debug 
、release、pre 版本的BASE_URL,请注意:

如果你执行了gradle assemblePre,没错是构建了 pre 版本,但是打印出日志你会发现:

app.BuildConfig.BASE_URL = "https://pre.api/"
module.BuildConfig.BASE_URL = "https://release.api/"
1
2
子模块中如果没有特别指定构建版本,无论你执行的是gradle assemblePre还是gradle assembleDebug,构建的都是 release 版本。可以使用defaultPublishConfig配置指定需要构建的版本,例如在子模块的 build.gradle 中指定:

android {
    ...
    //指定构建版本
    defaultPublishConfig "pre"
    ...
}
1
2
3
4
5
6
当然最好设置个变量,否则如果有许多子模块,不可能修改构建版本时一个一个改过去,常用的是在项目最外层的 build.gradle 中设置变量供子模块调用:

buildscript {
    ...
    dependencies {
        classpath 'com.android.tools.build:gradle:2.3.3'
    }
    ...
}
ext {
    projectBuildType = "debug"
}

//子模块中引用变量
defaultPublishConfig rootProject.ext.projectBuildType
1
2
3
4
5
6
7
8
9
10
11
12
13
这里需要注意,一旦子模块中指定了构建类型,例如 pre 版本,则该模块的 buildTypes 中必须也要有对应的构建类型 pre,否则编译不通过。并且,一旦指定了构建类型,则该模块的构建类型就只会是指定的类型。举个栗子:子模块中指定了defaultPublishConfig "pre",执行gradle assembleRelease,打印日志:

app.BuildConfig.BASE_URL = "https://release.api/"
module.BuildConfig.BASE_URL = "https://pre.api/"
1
2
所以这里就会比较烦人,每次打不同的包都需要去修改projectBuildType的值,还是需要手动修改。

分支名决定构建的版本类型

想要隔离手动修改,网上看到过一种解决方案: 
子模块的 build.gradle 中设置:

android {
    publishNonDefault true
}
1
2
3
然后在模块的依赖处,例如 app 模块的 build.gradle 中改进依赖的写法:

releaseCompile project(path: ':module', configuration: 'release')
debugCompile project(path: ':module', configuration: 'debug')
1
2
这样就可以实现构建时子模块与 app 模块的类型一致。但这种写法太烦了,如果你有十来个依赖,还得一个一个写过去,又如果你还自定义了不止一种构建类型,且没新增一个新的 buildType 都得修改所有的依赖,我认为也不是最优解。

这里提供一个方案仅供参考。先介绍一下我们的开发流程,例如新开发1.0版本。首先从 master 切出一条 devel1.0 分支用于前期开发阶段,开发完毕达到上线标准后,切出一条 pre1.0 预发分支,打预发包做最后测试并做最后的 bug 修复,最后测试通过,合并代码到 master 分支,打线上包发布至应用商店。不同阶段对应不同的分支,所以不同的构建版本可以通过分支名来决定,git 肯定可以获取当前分支名,而 grale 中则可以执行 cmd 命令,二者结合即可达到想要的效果。有这个思路后实现起来就很简单:

ext {
    projectBuildType = "debug"

    def gitBranchName = "git rev-parse --abbrev-ref HEAD".execute().text.trim()

    if(gitBranchName.contains("master")) {
        projectBuildType =  "release"
    } else if(gitBranchName.contains("pre")) {
        projectBuildType =  "pre"
    }
}
1
2
3
4
5
6
7
8
9
10
11
Gradle 3.0.0 带来的问题

Android Studio 3.0 + Gradle 3.0 相信许多人都跃跃欲试。升级到 Gradle 3.0 可能需要做一些改动,详情可见Migrate to Android Plugin for Gradle 3.0.0。 
Gradle3.0 中自定义 BuildType 有需要注意的地方

Cause of build error
Your app includes a build type that a library dependency does not.
在 Gradle 2.x 时代,如果 app 中定义了 pre 类型,而子模块中没有定义,是不会报错的。但在 Gradle 3.0 下,如果你的 app 包含了新的自定义的 buildType,而依赖库中却没有相应的自定义 buildType,则编译阶段就会报错。

一种解决方案是在子模块里也定义 app 中的所有 buildType,当然,项目里依赖多的同学肯定要吐槽了:我懒!不想修改辣么多东西! 
这里 Gradle 也提供了比 2.x 时代更智能的兼容方案:matchingFallbacks

在 buildTypes 中定义 matchingFallbacks,可以指定在子模块中没找到对应的构建类型时要加载哪个类型

//app 模块下的 build.gradle 中
buildTypes {
    debug {
        buildConfigField "String", "BASE_URL", "\"http://debug.api/\""
    }

    release {
        buildConfigField "String", "BASE_URL", "\"https://release.api/\""        
    }

    pre.initWith(release) 
    pre {
        buildConfigField "String", "BASE_URL", "\"https://pre.api/\""  
        matchingFallbacks = ['pre', 'debug', 'release']
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
matchingFallbacks 可以定义多个构建类型,当执行gradle assemblePre 构建 Pre 版本时,而恰巧某个子模块又没有定义 pre 版本,则会一一按照你指定的 matchingFallbacks 从前往后依次寻找,直到类型匹配。这种匹配方案比 Gradle 2.x 时代默认为你构建 release 版本要智能的多,至少我们还可以根据变量来在构建不同类型时定义不同的匹配顺序。并且我们也可以抛弃掉前文提到的defaultPublishConfig配置了,因为 Gradle 3.x 中,只要 gradle 构建时,子模块的构建类型变成了与 app 构建类型一致了(前提是子模块中也定义了该类型),变得更加灵活。

技术终归是在向前发展的。关于自定义 BuildType 的一些使用小贴士就是这些了,至于更多的关于构建类型相关的知识,例如 buildTypes 结合 productFlavors,或是定义 sourceSets 属性指定不同的代码目录、资源文件目录等知识以后有机会再开一篇聊吧(又挖坑2333)。

啰嗦了一堆,权且算是抛砖引玉。吼啦,下篇博客见~
--------------------- 
作者:扬州慢_ 
来源:CSDN 
原文:https://blog.csdn.net/yazhi1992/article/details/78859089 
版权声明:本文为博主原创文章,转载请附上博文链接!

本文转载自:https://blog.csdn.net/yazhi1992/article/details/78859089

共有 人打赏支持
猴亮屏

猴亮屏

粉丝 36
博文 527
码字总数 57346
作品 4
北京
Android工程师
私信 提问
Gradle之多版本打包不同依赖配置

一、构建变体 1. BuildType 1.1默认buildType 默认情况下还有一个debug版本,我们也可以添加对debug版本的一些设置 1.2自定义buildType 除了默认的构建版本,还可以创建自己的构建版本 其他属...

元谷
2017/11/02
0
0
Gradle实现Android多渠道定制化打包

最近在项目中遇到需要实现 Apk 多渠道、定制化打包, Google 、百度查找了一些资料,成功实现了上述功能,在此记录以备不时之需,温故而知新,可以为师矣~ 需求可以总结如下: 如何实现多个 ...

玄学酱
2017/08/03
0
0
capt 与 Android Gradle Plugin

capt 全称 Class Annotation Processor Tool, 是笔者开源的一个 Android 平台的字节码的注解处理工具,详情请了解 https://github.com/CoffeePartner/capt。 大家好,本篇会为大家介绍 capt...

任我行
01/07
0
0
android-gradle-plugin3.0.1源码分析

学习android的同学都知道android工程从使用android studio开发以后就使用了gradle作为工程的构建工具这就导致我们在了解gradle前提下还要对android-gradle-plugin这个插件有所了解 因为gradl...

qndroid
2018/07/05
0
0
寄Android开发Gradle你需要知道的知识

初识Gradle Gradle是一个基于Apache Ant和Apache Maven概念的项目自动化建构工具。它使用一种基于Groovy的特定领域语言来声明项目设置,而不是传统的XML。当前其支持的语言限于Java、Groovy和...

猴亮屏
2018/05/22
0
0

没有更多内容

加载失败,请刷新页面

加载更多

Docker的系统资源限制及验证

1 、限制容器的资源 默认情况下, 容器没有资源限制 ,可以使用主机内核调度程序允许的尽可能多的给定资源。 Docker 提供了控制容器可以 使用多少内存或 CPU 的方法 ,设置 docker run 命令的...

微笑向暖wx
13分钟前
1
0
Redis5.0之Stream案例应用解读

非常高兴有机会和大家在这里交流Redis5.0之Stream应用。今天的分享更多的是一个抛砖引玉,欢迎大家提出更多关于Redis的思考。 首先,我们来个假设,这里有个杯子,这个杯子是去年我老婆送的,...

中间件小哥
14分钟前
1
0
阿里开发者们的第20个感悟:好的工程师为人写代码,而不仅是为编译器

1月17日,好的工程师为人写代码,而不仅是为编译器。这是我们送给开发者的第20个感悟。 李响,作为开源项目etcd作者更为开发者所熟知。etcd是2013年由李响,Brandon Philips, Alex Polvi 所发...

阿里云官方博客
14分钟前
1
0
Linux vmstat命令详解

导读 Linux命令千千万,而我们在日常工作中真真切切用到的命令应该不超过50个,在接下来的日子里,我会对我经常使用的命令,以及使用过程中不熟悉的命令进行一个总结,一是自我总结,加深记忆...

问题终结者
15分钟前
1
0
MacOS Docker安装及使用

MacOS Docker 安装 Homebrew 安装 macOS 我们可以使用 Homebrew 来安装 Docker。 Homebrew 的 Cask 已经支持 Docker for Mac,因此可以很方便的使用 Homebrew Cask 来进行安装: # 安装命令...

火力全開
16分钟前
1
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部