阅读 1048

buildTypes——安卓gradle

目录
一、前言
二、buildTypes
三、buildType
1、buildTypes存在形式
2、buildTypes 中属性的意义
3、buildTypes 中方法的意义
四、写在最后

一、前言

上一篇博客 分享了defaultConfig 中可配置参数的含义,今天我们来分享另一个我们也很熟悉的 buildTypes

二、buildTypes

buildTypes 也是存在于每个应用级模块中的 android 下的,即如下所示,是每次构建完项目之后自动生成的结构。

android {
    buildTypes{
    	release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
}
复制代码

buildTypes 可以配置我们需要的构建类型,例如我们常用到的 “测试类型” 和 “本地类型”,则可以使用如下配置

buildTypes{
    // 发布类型
    release {
        minifyEnabled true
        proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
    }
    // 测试类型,给测试人员
    debug {
        minifyEnabled false
        proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
    }
    // 本地类型,和后端联调使用
    local {
        minifyEnabled false
        proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
    }
}
复制代码

增加完这些配置后,我们可以在 android studio 看到多了 “debug” 和 “local” 两个可以构建的类型,在 “点击运行” 时,便会使用我们所选择的构建类型。假设此时选择的是 “debug” 类型,我们此时运行代码,则是 debug 下的配置参数。

当然运行编译成 apk 时,也不例外,各自使用的也是各自类型的配置。

而这里所说的 “release”,“debug”,“local”,三个构建类型其实便是三个 buildTypebuildType 所能配置的参数便是我们今天要来捋清楚的。

三、buildType

buildType 官方文档传送门

1、buildType存在形式

从上一篇博客我们知道,每个配置最终会被映射为一个类,或是一个属性、或一个方法。buildType 也不例外,他会被映射为 com.android.build.gradle.internal.dsl.BuildType,继承结构如下

我们重新看一下 DefaultConfig 的继承结构,可以看到都会继承到 BaseConfigImpl 类,说明两者会有一定的交集。这也说明了为什么我们看 gradle 文件时,总感觉一个配置参数哪里都能出现的情况(后面会进行更多的比较,来解除我们这种疑惑)

2、buildType 中属性的意义

我们先来一个约定,避免使用的代码过于冗长。

buildTypes{
    debug {
	// 我们下面的 “使用方法” 代码都是基于这一块,除非特殊说明。
    }
}
复制代码

2.1 applicationIdSuffix

  • 类型:String
  • 描述:会追加在 applicationId 字符串的后面,形成最终的包名
  • 值得一提:在 defaultConfig 中也有这个属性,但一般不会使用。而会在 buildTypes 中使用,这样可以让包名不同,同时安装多个类型的应用。例如我们上面所提到的 release包、debug包、local包,都可以同时存在而不会覆盖,方便调试。
  • 使用方法:
debug {
    applicationIdSuffix '.debug'
}
复制代码

2.2 consumerProguardFiles

  • 类型:List< File >
  • 描述:这个属性只作用于我们创建的 library 中,包括我们以aar形式导入的 library ,或是直接创建的 library。它的作用是,负责该 library 被进行编译时的混淆规则,我们在 主App 的模块下则可以不用再管理各个 library 的混淆规则,会直接使用各个 library 的混淆规则文件。
  • 值得一提:这个属性 和 proguardFiles 的区别在于,consumerProguardFiles 会被 主App模块 作为混淆文件使用导入,而 proguardFiles 则不会。
  • 使用方法:
debug {
    consumerProguardFiles 'consumer-rules.pro'
    ......省略其他配置
}

// 因为该属性是一个 List<File> 类型,如果需要多个文件配置,则如下所示
debug {
    consumerProguardFiles 'consumer-rules.pro','zincPower-rules.pro'
    ......省略其他配置
}
复制代码

2.3 crunchPngs

  • 类型:boolean
  • 描述:是否对 PNG 图片进行压缩处理。设置为true时,会对未进行最佳压缩的PNG资源进行压缩,但也会增加构建时间。
  • 值得一提:默认情况下,release 类型是开启的,debug 类型则为关闭。
  • 使用方法:
debug {
    crunchPngs false
    ......省略其他配置
}
复制代码

2.4 debuggable

  • 类型:boolean
  • 描述:是否可以对应用进行调试。
  • 值得一提:release 的构建类型默认为不可调试,即为false。而 debug 默认为true,既可以调试。所谓的 无法调试 即下图的 “虫子” 标记无法让应用启动,而正常的运行是可以的。
  • 使用方法:
debug {
    debuggable true
}
复制代码

2.5 javaCompileOptions

  • 类型:JavaCompileOptions
  • 描述:配置编译时 java 的一些参数,例如我们使用 annotationProcessor 时所需要的参数。
  • 使用方法:
debug {
    javaCompileOptions {
        annotationProcessorOptions{
    		arguments = []
    		classNames ''
    		....
    	}
    }
    ......省略其他配置
}
复制代码

JavaCompileOptions 可以配置的具体参数,请进传送门

2.6 jniDebuggable

  • 类型:boolean
  • 描述:是否可以对应用的 native 代码进行调试
  • 值得一提:release 的构建类型默认为不可调试 native 代码,即为false。而 debug 默认为true,既可以调试 native 代码。无法对 native 代码调试,指的是即使在native方法打了断点,代码也不会被 “停住”。
  • 使用方法:
debug {
    jniDebuggable true
}
复制代码

2.7 manifestPlaceholders

  • 类型:Map<String, Object>
  • 描述:配置可以在 AndroidManifest.xml 中使用的参数。
  • 使用方法: 这里想配置我们应用的 logo 为测试版本的logo,方便测试人员区分不同类型的包,则可以在 gradle 中使用下面这段
debug {
    manifestPlaceholders = [APP_LOGO_ICON: "@mipmap/ic_logo"]
}
复制代码

然后在 AndroidManifest.xml 中使用,使用 ${你配置的变量名}

// 在 application 中使用替换,还需要多添加 tools:replace 这一标签,将我们需要替换的名称写上,例如这里的 android:icon
<application
    android:allowBackup="true"
    android:icon="${APP_LOGO_ICON}"
    android:label="@string/app_name"
    android:supportsRtl="true"
    android:theme="@style/AppTheme"
    tools:replace="android:icon">
......
复制代码

2.8 minifyEnabled

  • 类型:boolean
  • 描述:是否开启混淆,代码优化。true开始,false则关闭。
  • 使用方法:
debug {
    minifyEnabled true
}
复制代码

2.9 proguardFiles

  • 类型:List< File >
  • 描述:配置混淆规则文件,只有 minifyEnabled 设置为 true 的时候会使用这个参数,文件中需要申明哪些文件不被优化和混淆。
  • 值得一提:因为被混淆后端代码,类名和方法名都会有所变化,所以进行反射会失败,这是我们就需要进行申明他们不被混淆。(这里只是举了使用这个参数的一个场景,如果应用本来是正常的,开了混淆后,出现了莫名奇妙的bug,那就思考下是否因为混淆导致了这一bug
  • 使用方法:
debug {
    proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
复制代码

2.10 multiDexEnabled

64K 引用限制问题官方文档 传送门

  • 类型:Boolean
  • 描述:是否开启分包。因为安卓中方法索引值为两个字节,四位十六进制的一个数值,即[0, 0xffff],所以最大方法数为65536个。一旦超出了,就需要进行分包,所以我们就需要开启这个参数。
  • 使用方法:
android{
    buildTypes {
    	debug {
    		multiDexEnabled true
    		...
    	}
    }
}

// 添加依赖
dependencies {
    // 如果使用的为 AndroidX,则使用下面这个导入
    // implementation 'androidx.multidex:multidex:2.0.1'
    // 如果不使用 AndroidX,则使用下面这段
    compile 'com.android.support:multidex:1.0.3'
}
复制代码

有两种开启 MultiDex 方法:

// 第一种:让你应用的 Application 继承 MultiDexApplication。
public class MyApplication extends MultiDexApplication {

}

// 第二种:重写应用的 Application 方法 attachBaseContext
public class MyApplication extends Application {

    @Override
    protected void attachBaseContext(Context base) {
        super.attachBaseContext(base);
        MultiDex.install(this);
    }
}
复制代码

最后别忘了在 AndroidManifest.xml 中使用我们在上面的 Application。

2.11 multiDexKeepFile

  • 类型:File
  • 描述:将我们需要的类打包进主包,即 classs.dex。我们在第 2.10 小点,分享到使用了多包处理,有时我们需要将一些主要的类打包进主包,则可以使用该属性。
  • 使用方法:
debug {
    multiDexKeepFile file('multidex-config.txt')
    ...
}
复制代码

multidex-config.txt 中的书写则如下,每一个文件则为一行

com/example/MyClass.class
com/example/TestClass.class
复制代码

2.12 multiDexKeepProguard

  • 类型:File
  • 描述:将我们需要的类打包进主包,和第 2.11 点的功能相同,区别在于写法。
  • 使用方法:
debug {
    multiDexKeepFile file('multidex-config.pro')
    ...
}
复制代码

multidex-config.pro 中的写法如下

// 将会保留所有的在com.example package的类
-keep class com.example.** { *; }
复制代码

2.13 name

  • 类型:String
  • 描述:构建类型的名字。
  • 使用方法:
// debug 则会被赋值至 name 属性,而且 name 是final 类型,无法在改动。
debug{
    ...
}
复制代码

2.14 pseudoLocalesEnabled

  • 类型:boolean
  • 描述:这个属性用于开启伪语言,用于发现UI中潜在的可本地化问题。
  • 使用方法:

第一步:我们在配置中开启该属性

debug{
    pseudoLocalesEnabled true
}
复制代码

第二步:安装我们的应用 第三步:打开设置 -> 系统 -> 语言和输入法 -> 语言 -> 添加语言

会在 “添加语言” 页面中看到如下图的选项,选择图中红色框的内容,各自的不同在图中也有标明,我们往下走看看具体不同的表现是什么。

小盆友的测试机是 荣耀8,不同机子会有些许不同

选择之后,选择不同的语言会有不同效果,如图所示 (1)左边为未开启时的样子; (2)中间为选择了Pseudo locale(相当于en-XA); (3)右边为选择了Bidirection test locale(相当于en-XB);

2.15 renderscriptDebuggable

  • 类型:boolean
  • 描述:是否可以对 renderscript 代码进行调试
  • 使用方法:
debug {
    renderscriptDebuggable true
}
复制代码

2.16 renderscriptOptimLevel

  • 类型:int
  • 描述:设置渲染脚本等级
  • 使用方法:
debug {
    renderscriptOptimLevel 3
}
复制代码

2.17 shrinkResources

shrinkResources 官方使用手册 传送门

  • 类型:boolean
  • 描述:是否压缩资源,如果开启,gradle在编译时帮我们把没有使用的资源给移除。
  • 值得一提:shrinkResources 的开启需要 minifyEnabled 也为开启状态,否则无法运行。
  • 使用方法:
debug {
    shrinkResources false
}
复制代码

开启后编译完会看到如下日志

2.18 signingConfig

  • 类型:SigningConfig

SigningConfig 的可配置参数 传送门

  • 描述:配置签名配置。apk包能被安装是需要被签名的,我们直接运行的时候,是使用了系统默认的签名证书,当我们要发布release包时,则需要使用属于个人或企业的签名。
  • 使用方法:
buildTypes {
    release {
        signingConfig {
    	    // 不建议将签名证书的信息写在这里,而应该是写在 properties 文件中,将其引入使用
            config {
                keyAlias keystoreProperties['keyAlias']
                keyPassword keystoreProperties['keyPassword']
                storeFile file(keystoreProperties['storeFile'])
                storePassword keystoreProperties['storePassword']
            }
        }
        ...
    }
}
复制代码

2.19 testCoverageEnabled

  • 类型:boolean
  • 描述:测试覆盖率,可以获取测试覆盖率的报告
  • 值得一提:记得将 minifyEnabled 置为 false,否则报告也会是以混淆后的类名出现。
  • 使用方法:

第一步:在 build.gradle 中开启 testCoverageEnabled

debug {
    testCoverageEnabled true
}
复制代码

第二步:连接上一台可用设备,因为我们执行的测试代码是要基于设备的,如果不连接,测试阶段会报如下错

第三步:在 android studio 的 终端(即 Terminal)中输入下面这行命令,查看所有可运行任务。

小盆友是 mac 环境,所以 ./gradlew 开头。windows 的环境则直接使用 gradlew 即可。

./gradlew app:tasks
复制代码

输入运行后,可以看到图中高亮部分的可运行task。

我们以驼峰式(即第一个字母加上后面每个单词的首字母且以大写的形式)将其运行,代码如下

// create + 你的构建类型名(这里为debug) + CoverReport 
./gradlew app:cDCR
复制代码

运行完之后,就是查看测试报告的阶段了。我们进入coverage目录,具体路径如下图所示。

在这里插入图片描述
最后用浏览器打开 index.html 文件,就可以看报告啦,如下图所示,因为小盆友的这个项目没有写测试代码,所以覆盖率为0。
在这里插入图片描述

2.20 useProguard

  • 类型:boolean
  • 描述:是否开启总是开启 proguard
  • 使用方法:
debug {
    useProguard true
}
复制代码

2.21 versionNameSuffix

  • 类型:String
  • 描述:追加在 versionName 之后
  • 使用方法:
debug {
    // 如果 versionName "1.0.0" ,则最终的版本名为 1.0.0.test
    versionNameSuffix ".test"
    .....
}
复制代码

2.22 zipAlignEnabled

  • 类型:boolean
  • 描述:是否开启zipAlign。会对应用程序进行字节对齐,对齐后会减少了运行应用程序时消耗的内存。
  • 使用方法:
debug {
    zipAlignEnabled true
    .....
}
复制代码

2.23 matchingFallbacks

  • 类型:List< String >
  • 描述:用于处理 本地依赖library 间 BuildType 的冲突。

matchingFallbacks 还可以处理 维度风味的 问题,但我们这里先不讨论,后续的文章会介绍。

  • 使用方法:

一个东西的出现是为了处理至少一个问题,所以我们需要先了解下这个问题是怎么产生的。

如上图所示,我们的 主Module(一般是App),存在了三个构建类型,即我们一开始所提的 “release”、“debug”、“local” 三种。

此时我们在项目添加了一个 module 名字为 library,并依赖进我们的app中。当我们构建 “release”、“debug” 两种版本时,都不会有任何问题,因为 library 默认也提供了 “release”、“debug” 两种构建版本。

但是当我们使用 “local” 时,便会出问题了,因为此时 gradle 的脚本也会默认选择 “library” 的 “local”,但 “library” 中并没有。会报下面这样的错误

ERROR: Unable to resolve dependency for ':app@local/compileClasspath': Could not resolve project :lib:library.
Show Details
Affected Modules: app
复制代码

此时就需要帮 “local” 从 “library” 中选择一个可用的构建类型,则是通过 matchingFallbacks 进行设置。

铺垫了这么多,接下来就是怎么使用了,在 app 的 build.gradle 中添加如下代码即可。

local{
    matchingFallbacks = ['zinc','release']
    ...
}
复制代码

值得一提的是,我们可以配置多个,gradle在编译的时候,会按照从头开始匹配的原则,例如这里的 “zinc” 会匹配不到,则匹配 “release”,因为 “release” 匹配到了,则会进行使用,中断后面的匹配。

3、buildTypes 中方法的意义

3.1 buildConfigField(type, name, value)

  • 描述:我们可以在 BuildConfig 类中添加值,最终会在 BuildConfig 中添加如下一行代码。
// 值的注意的是 value 的值是原样放置,我们通过使用方法一节来了解
<type> <name> = <value>
复制代码
  • 使用方法:
debug {
    // 可以通过 BuildConfig 进行获取
    buildConfigField('String', 'name', '"zinc"')
    buildConfigField('int', 'age', '26')
    .....
}
复制代码

最终会生成如下图的配置,我们可以通过下面代码进行获取

String name = BuildConfig.name;
int age = BuildConfig.age;
复制代码

值的一提的是,我们设置 String 类型的参数时,需要加上 "" 双引号(如例子中的name属性)。切记!

3.2 consumerProguardFile(proguardFile)

  • 描述:和上面分享的 2.2 小点的属性 consumerProguardFiles 是一样的作用。只是这里只能设置一个 混淆文件。
  • 使用方法:
debug {
    consumerProguardFile('consumer-rules.pro')
}
复制代码

3.3 consumerProguardFiles(proguardFiles)

  • 描述:和上面分享的 2.2 小点的属性 consumerProguardFiles 是一样的作用,而且也是多个混淆文件。
  • 使用方法:
debug {
    consumerProguardFile('consumer-rules.pro', 'zincPower-rules.pro',.....)
}
复制代码

3.4 externalNativeBuild(action)

  • 类型:ExternalNativeBuildOptions
  • 描述:这里我们设置 ndk 编译过程的一些参数。分为 cmake 和 ndkBuild 两个参数。
  • 使用方法:
debug {
    externalNativeBuild {
        ndkBuild {
            // Passes an optional argument to ndk-build.
            arguments "NDK_MODULE_PATH+=../../third_party/modules"
        }
        // For ndk-build, instead use the ndkBuild block.
        cmake {
             // Passes optional arguments to CMake.
             arguments "-DANDROID_ARM_NEON=TRUE", "-DANDROID_TOOLCHAIN=clang"
        
             // Sets a flag to enable format macro constants for the C compiler.
             cFlags "-D__STDC_FORMAT_MACROS"
        
             // Sets optional flags for the C++ compiler.
             cppFlags "-fexceptions", "-frtti"
        
             // Specifies the library and executable targets from your CMake project
             // that Gradle should build.
             targets "libexample-one", "my-executible-demo"
         }
    }
}
复制代码

cmake 具体参数 传送门
ndkBuild 具体参数 传送门

3.5 initWith(that)

  • 描述:会拷贝给定的 buildType(即参数的值)
  • 使用方法:
buildTypes {
    debug{
    }
    local{
    	// 会拷贝 debug 的配置
        initWith debug{
          	// 在这里进行我们自己的配置
        }
    }
}
复制代码

3.6 proguardFile(proguardFile)

  • 描述:添加混淆文件,和 2.9小点 的功能一致,只是传入的是一个文件,这里就不再赘述
  • 使用方法:
 debug {
    proguardFile 'proguard-rules.pro'
}
复制代码

3.7 proguardFiles(files)

  • 描述:添加混淆文件,和 2.9小点 的功能一致,这里就不再赘述
  • 使用方法:
debug {
    proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
复制代码

3.8 setProguardFiles(proguardFileIterable)

  • 描述:添加混淆文件,和 2.9小点 的功能一致,只是写法稍微不同,这里就不再赘述
  • 使用方法:
debug {
    proguardFiles = [getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro']
}
复制代码

3.9 resValue(type, name, value)

  • 描述:添加 value 资源
  • 使用用法:
debug {
    // 添加至 res/value,通过 R.string.age 获取
    resValue('string', 'age', '12 years old')
}
复制代码

四、写在最后

Gradle 的配置文件看起来好像挺乱,其实是因为我们没有进行整体的梳理。很多配置其实不是没有用,而是我们没有对他有一个整体的了解,正所谓 “无知最可怕”

如果喜欢的话请给我一个赞,并关注我吧。文章中如有写的不妥的地方,请评论区或加我微信与我讨论吧,共同进步。

欢迎加我微信,进行更多的交流

如果觉得文章有很大的帮助,快来赞赏一次吧😄

关注下面的标签,发现更多相似文章
评论