1. Android Gradle 插件入门

学习 Gradle 的目的主要是为了更好的在 Android 开发中使用 Gradle,这篇文章的主要内容是 Android Gradle 插件的相关知识,主要内容如下:

  1. 了解 Android Gradle 插件
  2. Android Gradle 插件分类
  3. 使用 Android Gradle 插件
  4. Android Gradle 工程目录
  5. Android Gradle 基本配置
  6. Android Gradle 任务

了解 Android Gradle 插件

顾名思义,Android Gradle 插件就是一个用于 Android 构建项目的一个 Gradle 插件,该插件有 Google Android 开发团队开发,而且 Android 开发 IDE Android Studio 就采用 Gradle 构建项目,下面是 Android Gradle 插件的优势:

  1. 方便重用代码和资源;
  2. 可更方便的创建应用的衍生版本,如多渠道打包;
  3. 配置方便。可以扩展,还可以自定义构建过程
  4. Android Studio 与 Gradle 深度融合

Android Gradle 插件分类

Android 插件的分类是根据 Android 工程的属性进行分类的,Android 工程分为三类,具体如下:

  1. App 应用工程:可生成可运行的 apk ;
  2. Library 库工程:可生成 aar 共其他 App 应用工程使用,使用方式和 jar 一样,里面有相关的 Android 资源文件
  3. Test 测试工程:用于 App 应用过程或 Library 工程进行测试。

对应的就有三种不同的 Android Gradle 插件,三类插件分别是 App 插件、Library 插件和 Test 插件,其插件 id 如下:

  1. //App插件id
  2. com.android.application
  3. //Library插件id
  4. com.android.library
  5. //Test插件
  6. com.android.test

使用最多的肯定就是 App 插件和 Library 插件,使用这几个插件在 Android Studio 中就可以构建 Android 工程了。

使用 Android Gradle 插件

Gradle 插件使用时以插件的 id 作为唯一标识,如果是第三方的插件,还必须在 buildscript{} 中指定其 classpath,Android Gradle 插件就属于第三方插件,使用时要指定其 classpath,Android Gradle 插件托管到 jcenter 上,所以使用时要指定对应仓库,具体参考如下:

  1. buildscript {
  2. //配置插件仓库
  3. repositories {
  4. google()
  5. jcenter()
  6. }
  7. //配置依赖
  8. dependencies {
  9. classpath 'com.android.tools.build:gradle:3.0.1'
  10. }
  11. }

配置好第三方插件的仓库和依赖,然后使用 apply 使用该插件,具体如下:

  1. //使用App插件
  2. apply plugin: 'com.android.application'
  3. android {
  4. //编译所依赖的Android SDK的版本
  5. compileSdkVersion 26
  6. //构建工具的版本
  7. buildToolsVersion '26.0.2'
  8. //...
  9. }
  10. dependencies {
  11. //...
  12. }

在 Android 开发中一般将插件的仓库和依赖配置到根项目下的 build.gradle 文件中,其子 Module 项目将不需要在配置了,可直接使用,这里仅仅是 App 插件的使用,当然 Libray 插件的使用和 Test 插件的使用也是一样,使用时指定对应的 plugin id 即可。

Android Gradle 工程目录

下面是 Android Studio 创建 Android 工程的 Module 的目录,这也是 Android 工程的基本目录结构,参考如下:

  1. app
  2. ├─libs
  3. ├─proguard-rules.pro
  4. ├─build.gradle
  5. └─src
  6. ├─androidTest
  7. └─java
  8. ├─main
  9. ├─AndroidManifest.xml
  10. ├─java
  11. └─res
  12. └─test
  13. └─java

上述目录中 src 下面的 main、androidTest、test 是三个 SourceSet,main 表示 App 的源代码及资源目录,android 和 test 表示 Android 相关测试代码目录,此外 proguard-rules.pro 是当前项目的混淆文件,libs 用于存放 jar、aar 等库文件,build。gradle 文件用于当前配置文件。

Android Gradle 基本配置

为了介绍 Android Gradle 插件基本配置,使用 Android Studio 创建一个 Android 项目,下面的 app 的一个配置文件,具体如下:

  1. //使用Android Gradle插件
  2. apply plugin: 'com.android.application'
  3. //android{}是Android工程配置的入口
  4. android {
  5. /**
  6. * 指定编译依赖的Android SDK的版本,同
  7. * compileSdkVersion android-26
  8. * android.compileSdkVersion = 26
  9. * android.compileSdkVersion = 'android-26'
  10. */
  11. compileSdkVersion 26
  12. //指定构建工具的版本,也可以使用属性值buildToolsVersion设置其版本
  13. buildToolsVersion '26.0.2'
  14. /**
  15. * 默认配置,defaultConfig是一个ProductFlavor,可根据不同需求生成不同的Apk
  16. * 如果不自定义ProductFlavor进行单独配置,则该productFlavor会采用默认配置生成Apk
  17. * 这些具体配置,如applicationID都是ProductFlavor的属性
  18. */
  19. defaultConfig {
  20. //配置唯一包名
  21. applicationId "com.manu.androidgradleplugin"
  22. //最低支持的Android系统版本
  23. minSdkVersion 19
  24. //配置应用的目标Android系统版本
  25. targetSdkVersion 26
  26. //用于控制应用升级的版本号
  27. versionCode 1
  28. //用户看到的版本名称
  29. versionName "1.0"
  30. //测试时用到
  31. testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
  32. }
  33. /**
  34. * buildTypes是一个NamedDomainObjectContainer类型,是一个域对象,类似SourceSet(源集)
  35. * buildTypes中可自定义需要构建的类型,Gradle会自动创建一个相应的BuildType,如默认的release、debug
  36. */
  37. buildTypes {
  38. release {
  39. //设置是否为构建类型启用混淆
  40. minifyEnabled false
  41. //如果启用混淆则使用相应的混淆文件
  42. proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
  43. }
  44. }
  45. productFlavors {
  46. }
  47. }
  48. //依赖配置
  49. dependencies {
  50. implementation fileTree(include: ['*.jar'], dir: 'libs')
  51. implementation 'com.android.support:appcompat-v7:26.1.0'
  52. implementation 'com.android.support.constraint:constraint-layout:1.1.2'
  53. testImplementation 'junit:junit:4.12'
  54. androidTestImplementation 'com.android.support.test:runner:1.0.2'
  55. androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
  56. implementation files('libs/plugin.jar')
  57. }

上述配置文件中已经标注了一些 Android Gradle 插件需要配置的地方,也算是对 Android Gradle 插件及项目配置文件 build.gradle 的一次理解与学习,关于 Android Gradle 插件的基本配置就到此为止,以后陆续补充。

Android Gradle 任务

Android Gradle 插件基于 Android Gradle 插件,Android Gradle 插件中包含了 Java Gradle 插件的一些任务,如 assembile、check、build等任务,此外, Android Gradle 插件还添加一些新任务,如 connectedCheck、deviceCheck、lint、install、unInstall 等 Android 开发特有的任务,可在 Android Studio 上查看 Android 开发中一些任务,选中 Android Studio 右侧标签 Gradle 就可查看,参考如下:

Gradle(七) Android Gradle插件 - 图1

这篇算是初步从学习 Gradle 的角度了解了 Android Gradle 插件相关知识

2. Android Gradle 基础配置

上篇文章了解了 Android Gradle 插件的一下配置方法,记得刚开始接触 Android 中的 build.gradle 配置文件也是一脸懵逼,不知道各个配置项的具体含义,这篇文章将对 Android 开发中一些最基本的配置进行介绍,如果你有这方面的疑惑,相信这篇文章对你有一定收获

  1. 默认配置
  2. 配置签名信息
  3. 构建应用类型
  4. 使用混淆
  5. 启用zipalign优化

默认配置

defaultConfig 是 Android Gradle 配置文件中的一个配置块,defaultConfig 的类型是 ProductFlavor,如果没有自定义 ProductFlavor,则使用默认的 ProductFlavor 来配置 Android 工程,下面对 defaultConfig 中的一下配置属性进行介绍:

  1. //默认的ProductFlavor配置块
  2. defaultConfig {
  3. //配置App的包名
  4. applicationId "com.manu.base"
  5. //配合App最低支持的Android系统版本,下面两种minSdkVersion的配置效果是一致的
  6. minSdkVersion 19
  7. <!--minSdkVersion 'KitKat'-->
  8. //配置App基于哪个Android SDK开发
  9. targetSdkVersion 26
  10. //配置App的内部版本号,一般用于版本升级
  11. versionCode 1
  12. //配置App外部版本号,该版本号供用户查看
  13. versionName "1.0"
  14. //配置单元测试时使用的Runner
  15. testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
  16. //配置测试App的包名
  17. testApplicationId "com.manu.androidgradleproject.test"
  18. //使用指定的签名文件配置
  19. signingConfig signingConfigs.release
  20. }

配置签名信息

配置 App 签名信息的好处无非是防止 App 被恶意篡改,签名可保证 App 的唯一性且只有使用相同签名的后续升级包才能正常安装,在创建完签名文件之后,如果不做配置打包时每次都必须要指定签名文件的密码、别名等,一般 App 开发时在 denug 和 release 模式下时配置不同的签名文件。

第一步:创建签名证书文件,如下图所示填写相关信息:

第二步:使用 signConfigs 配置块配置已创建签名证书文件的相关信息如下:

  1. //签名文件配置
  2. signingConfigs {
  3. release{
  4. //签名证书文件
  5. storeFile file("project.jks")
  6. //签名证书文件密码
  7. storePassword "111111"
  8. //签名证书密钥别名
  9. keyAlias "project_jks"
  10. //签名证书中密钥密码
  11. keyPassword "111111"
  12. }
  13. debug{
  14. //默认情况下,debug模式下的签名已配置为Android SDK自动生成的debug签名文件证书
  15. //默认签名文件位置:.android/debug.keystore
  16. }
  17. }

第三步:使用签名文件配置,在 android{} 下 defaultConfig{} 中使用上述配置,具体如下:

  1. defaultConfig {
  2. //...
  3. //使用指定的签名文件配置
  4. signingConfig signingConfigs.release
  5. }

除了在 defaultConfig{} 中配置,还可以在分别在 denbug 或者是 release 模式下配置不同的签名文件,可在 buildTypes{} 中单独配置配置,具体如下:

  1. buildTypes {
  2. release {
  3. signingConfig signingConfigs.release
  4. //...
  5. }
  6. debug{
  7. signingConfig signingConfigs.debug
  8. //...
  9. }
  10. //...
  11. }

构建应用的类型

Android Gradle 内置了两种构建类型 debug 和 release,两者区别是前者一般用在调试状态,后者一般用于正式发布状态,其他方面都一样,那么如何增加新的构建类型呢,可直接在 buildTypes{} 中添加要添加的类型即可,buildTypes 接收的参数是一个域对象,添加的构建类型都是 BuildType,所以可以通过 BuildType 的相关属性来配置构建类型,下面是 BuildType 的常见的一些配置属性:

  1. buildTypes {
  2. release {
  3. //...
  4. }
  5. debug{
  6. //配置签名
  7. signingConfig signingConfigs.debug
  8. //配置在当前构建类型下applicationId的后缀,构建生成Apk的包名会在applicationId的基础上添加后缀
  9. applicationIdSuffix '.debug'
  10. //配置是否生成一个可供调试的Apk
  11. denbuggable true
  12. //配置是否生成一个可供调试jni(c/c++)代码的Apk
  13. jniDebuggable true
  14. //是否启用proguard混淆
  15. minifyEnabled true
  16. //配置当程序中方法数超过65535个时,是否启用自动拆分多个dex的功能,
  17. multiDexEnabled true
  18. //配置proguard混淆使用的配置文件,可配置对个混淆文件
  19. proguardFiles getDefaultProguardFile('proguard-android.txt'),'proguard-rules.pro'
  20. //配置是否自动清理未使用的资源,默认为false
  21. shrinkResources true
  22. //开启zipalign优化(见下文)
  23. zipAlignEnabled true
  24. }
  25. }

当在 buildTypes{} 中添加新的构建类型之后,Android Gradle 都会自动生成一个 SourceSet,构建 Apk 会从对应的 SourceSet 中进行构建,切记新构建类型的名称不能和已有的相同。且要在 src 目录下为新创建的构建类型添加源代码目录和资源文件等,在创建好构建类型的同时,Android Gradle 插件也会生成对应的 assemble 任务来用于构建该类型的项目,如 release 对应的是 assembleRelease,执行该任务会生成对应的 Apk.

使用混淆

代码混淆主要了增加反编译的难度,发布正式版 App 时一般都得进行代码混淆,实际上 Android SDK 下面已经提供了默认的混淆文件,具体位置在 Android SDK 下面的 tools/progrard 下面,里面的内容主要是一些最基本的不能混淆的内容,两个默认的混淆文件如下:

  1. //没优化
  2. proguard-android.txt
  3. //已优化
  4. proguard-android-optimize.txt

那么如何使用混淆呢,在 buildTypes{} 中对应的构建类型下设置 minifyEnabled 为 true 开启混淆,然后配置具体的混淆文件,具体如下:

  1. buildTypes {
  2. release {
  3. //开启混淆
  4. minifyEnabled false
  5. //配置混淆文件
  6. proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
  7. }
  8. //...
  9. }

启用 zipalign 优化

zipalign 是 Android 提供的一个整理优化 apk 文件的工具,可在一定程度上上提高系统和应用的运行效率,更快的读取 apk 中的资源,降低内存的使用,开启 zipalign 优化只需要在 buildTypes{} 中对应的构建类型下开启 zipalign 优化即可,具体如下:

  1. buildTypes {
  2. release {
  3. //开启zipalign优化
  4. zipAlignEnabled true
  5. //''
  6. }
  7. //...
  8. }

这篇算介绍了 Android 开发中一些常见配置项的介绍。

3. Android Gradle高级配置

本篇文章主要在之前学习的基础上,从实际开发的角度学习如何对 Android Gradle 来进行自定义以满足不同的开发需求下面是主要内容:

  1. 修改生成的Apk文件名
  2. 版本信息统一管理
  3. 隐藏签名文件信息
  4. 动态配置AndroidManifest文件
  5. 自定义BuildConfig
  6. 动态添加自定义资源
  7. Java编译选项
  8. adb操作选项配置
  9. DEX选项配置
  10. 自动起立未使用的资源
  11. 突破65535方法限制

修改生成的Apk文件名

修改打包输出的 Apk 的文件名主要用到三个属性:

  1. applicationVariants //Android应用Gradle插件
  2. libraryVariants //Android库Gradle插件
  3. testVariants //上述两种插件都适用

下面是修改打包生成的 Apk 文件名的代码,参考如下:

  1. android{
  2. //...
  3. /**
  4. * 修改打包生成的apk的文件名
  5. */
  6. applicationVariants.all { variant ->
  7. variant.outputs.all { output ->
  8. if (output.outputFile != null && output.outputFile.name.endsWith('.apk') &&
  9. 'release' == variant.buildType.name) {
  10. //输出文件名
  11. outputFileName = "AndroidGradleProject_v${variant.versionName}_${buildTime()}.apk"
  12. }
  13. }
  14. }
  15. }
  16. //当前时间
  17. def static buildTime() {
  18. def date = new Date()
  19. return date.format("yyyMMdd")
  20. }

此时,执行 release 模式构建 Apk 的任务,生成的 Apk 的名字就修改了,当然还可以配置在 debug 模式下生成对应的文件名等。

版本信息统一管理

每个应用都有一个版本,版本一般由三部分组成:major.minor.patch,第一个是主版本号,第二个是副版本号,第三个是补丁号,如 1.0.0 这种格式的版本号,在 Android 开发中最原始的版本配置方式就是在 build.gradle 中在 defaultConfig 中配置对应的版本号和版本名称,参考如下:

  1. //最原始的版本配置方式
  2. android{
  3. defaultConfig {
  4. versionCode 1
  5. versionName "1.0"
  6. //...
  7. }
  8. }

实际开发中一般将这种版本相关的信息单独定义在一个独立的版本管理文件中进行统一管理,定义 version.gradle 文件如下:

  1. ext{
  2. //应用版本号、版本名称
  3. appversionCode = 1
  4. appVersionName = "1.0"
  5. //其他版本号...
  6. }

然后在 build.gradle 中使用 version.gradle 文件中定义的版本号、版本名称即可,参考如下:

  1. //引入version.gradle文件
  2. apply from: "version.gradle"
  3. android {
  4. //...
  5. defaultConfig {
  6. //使用version.gradle里定义的版本号
  7. versionCode appversionCode
  8. //使用version.gradle里定义的版本名称
  9. versionName appVersionName
  10. //...
  11. }
  12. }

当然不只是应用的版本号,还有使用到的一些第三方的库的版本也可以使用这样的方式来统一管理。

隐藏签名文件信息

签名文件信息是非常重要的信息,如果将签名文件信息直接配置到项目中将是不安全的,那么签名文件如何能够安全呢,签名文件放在本地是不安全的,那么只能放在服务器上才是安全的,打包的时候从服务器上读取签名文件信息即可,当然这个服务器也可以是一台专门用于打包正式 Apk 的电脑,将签名文件和密钥信息配置成环境变量,打包是直接从环境变量中读取签名文件和密钥信息即可。

配置四个环境变量 STORE_FILE、STORE_PASSWORD、KEY_ALIAS、KEY_PASSWORD 分别对应签名文件、签名文件密码、签名文件密钥别名、签名文件密钥密码,环境变量的配置就不具体说了,代码参考如下:

  1. android {
  2. //签名文件配置
  3. signingConfigs {
  4. //读取配置的与签名文件信息对应的环境变量
  5. def appStoreFile = System.getenv('STORE_FILE')
  6. def appStorePassword = System.getenv('STORE_PASSWORD')
  7. def appKeyAlias = System.getenv('KEY_ALIAS')
  8. def appKeyPassword = System.getenv('KEY_PASSWORD')
  9. //如果获取不到相关签名文件信息,则使用默认的签名文件
  10. if(!appStoreFile || !appStorePassword || !keyAlias || !keyPassword){
  11. appStoreFile = "debug.keystore"
  12. appStorePassword = "android"
  13. appKeyAlias = "androiddebugkey"
  14. appKeyPassword = "android"
  15. }
  16. release {
  17. storeFile file(appStoreFile)
  18. storePassword appStorePassword
  19. keyAlias appKeyAlias
  20. keyPassword appKeyPassword
  21. }
  22. debug {
  23. //默认情况下,debug模式下的签名已配置为Android SDK自动生成的debug签名文件证书
  24. //.android/debug.keystore
  25. }
  26. }
  27. }

注意一点,配置好环境变量后,如果不能读取到新配置的环境变量,重启电脑后就能读取到了,至于如何使用专用的服务器进行打包、读取签名文件信息实践后再来介绍。

动态配置AndroidManifest文件

动态配置 AndroidManifest 配置就是动态的去修改 AndroidManifest 文件中的一些内容,如友盟等第三方统计平台分析统计的时候,一般会要求要在 AndroidManifest 文件中指定渠道名称,如下所示:

  1. <meta-data android:value="CHANNEL_ID" android:name="CHANNEL"/>

这里 CHANNEL_ID 要替换成不同渠道的名称,如 baidu、miui 等各个渠道名称,那么如何动态的修改这些变化的参数呢,这里需要用到 Manifest 占位符和 manifestPlaceholder,manifestPlaceholder 是 ProductFlavor 的一个属性,是一个 Map 类型,可以配置多个占位符,具体代码参考如下:

  1. android{
  2. //维度
  3. flavorDimensions "channel"
  4. productFlavors{
  5. miui{
  6. dimension "channel"
  7. manifestPlaceholders.put("CHANNEL","google")
  8. }
  9. baidu{
  10. dimension "channel"
  11. manifestPlaceholders.put("CHANNEL","baidu")
  12. }
  13. }
  14. }

上述代码中配置了 flavorDimensions 属性,这个属性可以理解为维度,比如 release 和 debug 是一个维度、不同的渠道是一个维度、免费版本还是付费版本又是一个维度,如果这三个维度都要考虑,那么生成 Apk 的格式就是 2 2 2 供 8 个不同的 Apk,从 Gradle 3.0 开始不管是一个维度还是多个维度,都必须使用 flavorDimensions 来约束,上面代码中定义了一个维度 channel,再加上 buildType 中的 debug 和 release ,故此时生成不同 Apk 的个数是 4 个,如下图所示:

Gradle(七) Android Gradle插件 - 图2

当然,如果没有配置 flavorDimensions 则会出现如下错误,具体如下:

Error:All flavors must now belong to a named flavor dimension.

实际开发中根据实际情况配置对应的 flavorDimensions 即可。

然后,在 AndroidManifest 文件中使用占位符介绍打包时传递过来的参数,在 AndroidManifest 文件中添加 如下:

  1. <meta-data android:value="${CHANNEL}" android:name="channel"/>

最后,执行对应的渠道包任务,如执行 assembleBaiduRelease 将会将 AndroidManifest 中的渠道替换成 baidu,可使用命令执行也可使用 Android Studio 选择对应的 task 来执行,执行命令如下:

  1. gradle assembleBaiduRelease

如果使用 Android Studio ,打开右侧 Gradle 控制面板,找到对应的 task 来执行相应的任务,如下图所示:

Gradle(七) Android Gradle插件 - 图3

选择对应的 task 执行就会生成对应的 Apk,使用 Android Killer 反编译打开生成的 Apk ,查看 AndroidManifest 文件如下:

  1. <?xml version="1.0" encoding="utf-8" standalone="no"?>
  2. <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.manu.androidgradleproject">
  3. <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:supportsRtl="true" android:theme="@style/AppTheme" roundIcon="@mipmap/ic_launcher_round">
  4. <!--AndroidManifest文件修改成功-->
  5. <meta-data android:name="channel" android:value="baidu"/>
  6. <activity android:name="com.manu.androidgradleproject.MainActivity">
  7. <intent-filter>
  8. <action android:name="android.intent.action.MAIN"/>
  9. <category android:name="android.intent.category.LAUNCHER"/>
  10. </intent-filter>
  11. </activity>
  12. <meta-data android:name="android.support.VERSION" android:value="26.1.0"/>
  13. <meta-data android:name="android.arch.lifecycle.VERSION" android:value="27.0.0-SNAPSHOT"/>
  14. </application>
  15. </manifest>

上述案列中,渠道的名称是一致的,可以通过遍历很方便的完成渠道名称的替换,参考如下:

  1. productFlavors.all{ flavor ->
  2. manifestPlaceholders.put("CHANNEL",name)
  3. }

这一小节重要的一点就是关于 manifestPlaceholders 占位符的使用。

自定义BuildConfig

BuildConfig 是一个在 Android Gradle 构建脚本编译后生成的类,默认构建生成的 BuildConfig 内容如下:

  1. /**
  2. * Automatically generated file. DO NOT MODIFY
  3. */
  4. package com.manu.androidgradleproject;
  5. public final class BuildConfig {
  6. public static final boolean DEBUG = false;
  7. public static final String APPLICATION_ID = "com.manu.androidgradleproject";
  8. public static final String BUILD_TYPE = "release";
  9. public static final String FLAVOR = "baidu";
  10. public static final int VERSION_CODE = 1;
  11. public static final String VERSION_NAME = "1.0";
  12. }

上面 BuildConfig 中的一些常量都是关于应用的一些关键信息,其中 DEBUG 在 debug 模式下为 true,release 模式下为 false,此外还有应用包名、构建类型、构建渠道、版本号及版本名称,所以如果开发中需要用到这些值可以在 BuildConfig 中直接获取,比如包名的获取一般是 context.getPackageName(),如果直接从 BuildConfig 中获取是不是不仅方便而且有利于应用性能提升,所以,可在构建时在该文件中添加一些额外的有用的信息,可以使用 buildConfigField 方法,具体如下:

  1. /**
  2. * type:生成字段的类型
  3. * name:生成字段的常量名称
  4. * value:生成字段的常量值
  5. */
  6. public void buildConfigField(String type, String name, String value) {
  7. //...
  8. }

下面使用 buildConfigField 方法为每个渠道配置一个相关地址,参考如下:

  1. android{
  2. //维度
  3. flavorDimensions "channel"
  4. productFlavors{
  5. miui{
  6. dimension "channel"
  7. manifestPlaceholders.put("CHANNEL","miui")
  8. buildConfigField 'String' ,'URL','"http://www.miui.com"'
  9. }
  10. baidu{
  11. dimension "channel"
  12. manifestPlaceholders.put("CHANNEL","baidu")
  13. //buildConfigField方法参数value中的内容是单引号中的,如果value是String,则String的双引号不能省略
  14. buildConfigField 'String' ,'URL','"http://www.baidu.com"'
  15. }
  16. }
  17. }

再打包时就会自动生成添加的字段,构建完成后查看 BuildConfig 文件,生成了上面添加的字段,参考如下:

  1. /**
  2. * Automatically generated file. DO NOT MODIFY
  3. */
  4. package com.manu.androidgradleproject;
  5. public final class BuildConfig {
  6. public static final boolean DEBUG = false;
  7. public static final String APPLICATION_ID = "com.manu.androidgradleproject";
  8. public static final String BUILD_TYPE = "release";
  9. public static final String FLAVOR = "baidu";
  10. public static final int VERSION_CODE = -1;
  11. public static final String VERSION_NAME = "";
  12. // Fields from product flavor: baidu
  13. public static final String URL = "http://www.baidu.com";
  14. }

至此,自定义 BuildConfig 的学习就到此为止,当然 buildConfigField 也可以使用到构建类型中,关键就是 buildConfigField 方法的使用。

动态添加自定义资源

Android 开发中资源文件都是放置在 res 目录下,还可以在 Android Gradle 中定义,自定义资源需要使用到 resValue 方法,该方法在 BuildType 和 ProductFlavor 对象中可以使用,使用 resValue 方法会生成相对应的资源,使用方式和在 res/values 文件中定义的一样

  1. android{
  2. //...
  3. productFlavors {
  4. miui {
  5. //...
  6. /**
  7. * resValue(String type,String name,String value)
  8. * type:生成字段的类型(id、string、bool等)
  9. * name:生成字段的常量名称
  10. * value:生成字段的常量值
  11. */
  12. resValue 'string', 'welcome','miui'
  13. }
  14. baidu {
  15. //...
  16. resValue 'string', 'welcome','baidu'
  17. }
  18. }
  19. }

当生成不同的渠道包时,通过 R.string.welcome 获取的值是不相同的,如生成的百度的渠道包时 R.string.welcome 的值为 baidu、生成小米渠道包时 R.string.welcome 的值为 miui,构建时生成的资源的位置在 build/generated/res/resValues/baidu/… 下面的 generated.xml 文件中,文件内容参考如下:

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <resources>
  3. <!-- Automatically generated file. DO NOT MODIFY -->
  4. <!-- Values from product flavor: baidu -->
  5. <string name="welcome" translatable="false">baidu</string>
  6. </resources>

Java编译选项

在 Android Gradle 中还可以配置 Java 源代码的编译版本,这里使用到 compileOptions 方法, compileOptions 可配置三个属性:encoding、sourceCompatibility 和 targetCompatibility,通过这些属性来配置 Java 相关的编译选项,具体参考如下:

  1. //配置Java编译选项
  2. android {
  3. compileSdkVersion 26
  4. buildToolsVersion '26.0.2'
  5. compileOptions{
  6. //设置源文件的编码
  7. encoding = 'utf-8'
  8. //设置Java源代码的编译级别()
  9. sourceCompatibility = JavaVersion.VERSION_1_8
  10. // sourceCompatibility "1.8"
  11. // sourceCompatibility 1.8
  12. // sourceCompatibility "Version_1_8"
  13. //设置Java字节码的版本
  14. targetCompatibility = JavaVersion.VERSION_1_8
  15. }
  16. }

adb操作选项设置

adb 的全称是 Android Debug Bridge,adb 主要用来连接手机来进行一些操作,比如调试 Apk、安装 Apk、复制文件等操作,在 Android Gradle 中可借助 adpOptions 来配置,可配置的有两个属性:installOptions 和 timeOutInMs,也可以通过相应的 setter 方法来设置,具体参考如下:

  1. android{
  2. //adb配置选项
  3. adbOptions{
  4. //设置执行adb命令的超时时间
  5. timeOutInMs = 5 * 1000
  6. /**
  7. * 设置adb install安装这个操作的设置项
  8. * -l:锁定应用程序
  9. * -r:替换已存在的应用程序
  10. * -t:允许测试包
  11. * -s:把应用程序安装到SD卡上
  12. * -d:允许应用程序降级安装
  13. * -g:为该应用授予所有运行时的权限
  14. */
  15. installOptions '-r', '-s'
  16. }
  17. }

installOptions 的配置对应 adb install [-lrtsdg] 命令,如果安装、运行或调试 Apk 的时候,如果出现 CommandRejectException 可以尝试设置 timeOutInMs 来解决,单位是毫秒。

DEX选项配置

Android 中的源代码被编译成 class 字节码,在打包成 Apk 的时候又被 dx 命令优化成 Android 虚拟机可执行的 DEX 文件,DEX 格式的文件是专为 Android 虚拟机设计的,在一定程度上会提高其运行速度,默认情况下给 dx 分配的内存是 1024M,在 Android Gradle 中可以通过 dexOptions 的五个属性:incremental、javaMaxHeapSize、jumboMode、threadCount 和 preDexLibraries 来对 DEX 进行相关配置,具体参考如下:

  1. android{
  2. //DEX选项配置
  3. dexOptions{
  4. //设置是否启用dx增量模式
  5. incremental true
  6. //设置执行dx命令为其分配的最大堆内存
  7. javaMaxHeapSize '4g'
  8. //设置是否开启jumbo模式,如果项目方法数超过65535,需要开启jumbo模式才能构建成功
  9. jumboMode true
  10. //设置Android Gradle运行dx命令时使用的线程数量,可提高dx执行的效率
  11. threadCount 2
  12. /**
  13. * 设置是否执行dex Libraries库工程,开启后会提高增量构建的速度,会影响clean的速度,默认为true
  14. * 使用dx的--multi-dex选项生成多个dex,为避免和库工程冲突,可设置为false
  15. */
  16. preDexLibraries true
  17. }
  18. }

自动清理未使用资源

Android 开发中打包 Apk 总是希望在相同功能的情况下 Apk 体积尽量小,那就要在打包之前删除没有使用的资源文件或打包时不将无用的资源打包到 Apk 中,可以使用 Android Lint 检查未使用的资源,但是无法清除一些第三方库中的无用资源,还可以使用 Resource Shrinking,可在打包之前检查资源,如果没有使用则不会被打包到 Apk 中,具体参考如下:

  1. //自动清理未使用资源
  2. android{
  3. buildTypes {
  4. release {
  5. //开启混淆,保证某些资源在代码中未被使用,以便于自动清理无用资源,两者配合使用
  6. minifyEnabled true
  7. /**
  8. * 打包时会检查所有资源,如果没有被引用,则不会被打包到Apk中,会处理第三方库不使用的资源
  9. * 默认不启用
  10. */
  11. shrinkResources true
  12. //开启zipalign优化
  13. proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
  14. }
  15. debug{
  16. }
  17. }
  18. //...
  19. }

为防止有用资源未被打包到 Apk 中,Android Gradle 提供了 keep 方法来配置那些资源不被清理,在 res/raw/ 下新建一个 xml 文件来使用 keep 方法,参考如下:

  1. <!--keep.xml文件-->
  2. <?xml version="1.0" encoding="utf-8"?>
  3. <resources xmlns:tools="http://schemas.android.com/tools"
  4. tools:keep="@layout/l_used*_c,@layout/l_used_a,@layout/l_used_b*"
  5. tools:discard="@layout/l_used"
  6. tools:shrinkMode="safe"/>

可配置的三个属性:keep 表示要保留的资源文件,可使用以(,)分割的资源列表,可使用(*)作为通配符,discard 表示要移除的资源,和 keep 类似,shrinkMode 用于设置自动清理资源的模式,一般设置为 safe 即可,如果设置为 strict 则有可能清除可能会使用的资源。

此外,还可以使用 ProductFlavor 提供的方法 resConfigs 和 resConfig,可配置那些资源打包到 Apk 中,使用方式如下:

  1. android{
  2. defaultConfig{
  3. //参数可以是Android开发时的资源限定符
  4. resConfigs 'zh'
  5. //...
  6. }
  7. }

上述自动清理资源的方式只是不打包到 Apk 中,在实际的项目中并没有被清除,可通过日志查看哪些资源被清理了,然后决定要不要在项目中清除。

突破65535方法限制

在 Android 开发中总会遇到方法数大于 65535 时出现异常,那为什么会有这个限制呢,因为 Java 源文件被打包成一个 DEX 文件,这个文件是优化过的、可在 Dalvik 虚拟机上可执行的文件,由于 Dalvik 在执行 DEX 文件的时候,使用了 short 来索引 DEX 文件中的方法,这就意味着单个 DEX 文件可被定义的方法最多只有 65535 个。解决办法自然是当方法数超过 65535 个的时候创建多个 DEX 文件。

从 Android 5.0 开始的 Android 系统使用 ART 的运行方式,原生支持多个 DEX 文件,ART 在安装 App 的时候执行预编译,把多个 DEX 文件合并成一个 oat 文件执行,在 Android 5.0 之前,Dalvik 虚拟机只支持单个 DEX 文件,要想突破单个 DEX 方法数超过 65535 的限制,需使用 Multidex 库,这里就不在赘述了。