android studio gradle目錄_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”,三個構建型別其實便是三個buildType
,buildType
所能配置的引數便是我們今天要來捋清楚的。
三、buildType
buildType 官方文件
https://google.github.io/android-gradle-dsl/3.3/com.android.build.gradle.internal.dsl.BuildType.html
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 型別,如果需要多個檔案配置,則如下所示
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 可以配置的具體引數,請進
https://google.github.io/android-gradle-dsl/3.3/com.android.build.gradle.internal.dsl.JavaCompileOptions.html
2.6 jniDebuggable
型別:boolean
描述:是否可以對應用的 native 程式碼進行除錯
值得一提:release 的構建型別預設為不可除錯 native 程式碼,即為false。而 debug 預設為true,既可以除錯 native 程式碼。無法對 native 程式碼除錯,指的是即使在native方法打了斷點,程式碼也不會被 “停住”。
使用方法:
debug {
jniDebuggable true
}
2.7 manifestPlaceholders
型別:Map
描述:配置可以在
AndroidManifest.xml
中使用的引數。使用方法:這裡想配置我們應用的 logo 為測試版本的logo,方便測試人員區分不同型別的包,則可以在 gradle 中使用下面這段
debug {
manifestPlaceholders = [APP_LOGO_ICON: "@mipmap/ic_logo"]
}
然後在AndroidManifest.xml
中使用,使用${你配置的變數名}
// 在 application 中使用替換,還需要多新增 tools:replace 這一標籤,將我們需要替換的名稱寫上,例如這裡的 android:icon
<applicationandroid: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 引用限制問題官方文件
https://developer.android.com/studio/build/multidex
型別: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 官方使用手冊
https://developer.android.com/studio/build/shrink-code?utm_source=android-studio#shrink-resources
型別:boolean
描述:是否壓縮資源,如果開啟,gradle在編譯時幫我們把沒有使用的資源給移除。
值得一提:shrinkResources 的開啟需要 minifyEnabled 也為開啟狀態,否則無法執行。
使用方法:
debug {
shrinkResources false
}
開啟後編譯完會看到如下日誌
2.18 signingConfig
型別:SigningConfig
SigningConfig 的可配置引數:https://google.github.io/android-gradle-dsl/3.3/com.android.build.gradle.internal.dsl.SigningConfig.html
描述:配置簽名配置。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 ':[email protected]/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 的值是原樣放置,我們通過使用方法一節來了解
=
使用方法:
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 具體引數文件:
https://google.github.io/android-gradle-dsl/3.3/com.android.build.gradle.internal.dsl.ExternalNativeCmakeOptions.html
ndkBuild 具體引數文件:
https://google.github.io/android-gradle-dsl/3.3/com.android.build.gradle.internal.dsl.ExternalNativeNdkBuildOptions.html
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 的配置檔案看起來好像挺亂,其實是因為我們沒有進行整體的梳理。很多配置其實不是沒有用,而是我們沒有對他有一個整體的瞭解,正所謂“無知最可怕”。
如果喜歡的話請給我一個贊,並關注我吧。文章中如有寫的不妥的地方,請評論區與我討論吧,共同進步。
專案地址:https://github.com/zincPower/GradleStudy