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,繼承結構如下

BuildType
DefaultBuildType
CoreBuildType
接口BuildType
BaseConfigImpl
BaseConfig

我們重新看一下 DefaultConfig 的繼承結構,可以看到都會繼承到 BaseConfigImpl 類,說明兩者會有一定的交集。這也說明了爲什麼我們看 gradle 文件時,總感覺一個配置參數哪裏都能出現的情況(後面會進行更多的比較,來解除我們這種疑惑)

DefaultConfig
BaseFlavor
DefaultProductFlavor
CoreProductFlavor
BaseConfigImpl
ProductFlavor
BaseConfig
DimensionAware

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 的配置文件看起來好像挺亂,其實是因爲我們沒有進行整體的梳理。很多配置其實不是沒有用,而是我們沒有對他有一個整體的瞭解,正所謂 “無知最可怕”

如果喜歡的話請給我一個贊,並關注我吧。文章中如有寫的不妥的地方,請評論區或加我微信與我討論吧,共同進步。

歡迎加我微信,進行更多的交流

如果覺得文章有很大的幫助,快來讚賞一次吧😄

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章