
Android Studio 目錄層級(jí)
├── app #Android App目錄
│ ├── app.iml
│ ├── build #構(gòu)建輸出目錄
│ ├── build.gradle #構(gòu)建腳本
│ ├── libs #so相關(guān)庫
│ ├── proguard-rules.pro #proguard混淆配置
│ └── src #源代碼,資源等
├── build
│ └── intermediates
├── build.gradle #工程構(gòu)建文件
├── gradle
│ └── wrapper
├── gradle.properties #gradle的配置 外部屬性
├── gradlew #gradle wrapper linux shell腳本
├── gradlew.bat
├── LibSqlite.iml
├── local.properties #配置Androod SDK位置文件
└── settings.gradle #工程配置
settings.gradle
settings.gradle用于配置 project。settings 文件聲明了所需的配置來實(shí)例化項(xiàng)目的層次結(jié)構(gòu),標(biāo)明其下有幾個(gè) module,比如這里包含一個(gè):appmodule。
include ':app'
根目錄的 build.gradle
和
settings.gradle在同一目錄下的build.gradle是一個(gè)頂級(jí)的 build 配置文件,在這里可以為所有的 project 以及 module 配置一些常用的配置。
根目錄中的 build.gradle 默認(rèn)包含 2 個(gè)代碼塊
-
buildscript{}用于配置構(gòu)建腳本所用到的代碼庫和依賴關(guān)系。這里定義了 Android 編譯工具的類路徑。repositories 中, jCenter 是一個(gè)著名的 Maven 倉庫。 -
allprojects{}用于定義所有模塊需要用到的一些公共屬性。這里定義的屬性會(huì)被應(yīng)用到所有的 module 中,但為了保證每個(gè)項(xiàng)目的獨(dú)立性,一般不會(huì)在這里操作太多共有的東西
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
jcenter()//使用 jcenter庫
}
dependencies {
// 依賴 android提供的 1.1.0 的 gradle build
classpath 'com.android.tools.build:gradle:1.1.0'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
//為所有的工程的 repositories 配置為 jcenters
allprojects {
repositories {
jcenter()
}
}
模塊級(jí) build.gradle
除了更目錄下的 build 文件,gradle 支持對每個(gè) module 進(jìn)行配置,主要有三個(gè)重要的代碼塊:plugin、android 和 dependencies
apply plugin: 'com.android.application'
android {
compileSdkVersion 21
buildToolsVersion "22.0.1"
defaultConfig {
applicationId "org.flysnow.demo"
minSdkVersion 9
targetSdkVersion 21
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:appcompat-v7:22.0.0'
}
apply plugin
第一行 apply plugin: 'com.android.application',這表示該 module 是一個(gè) app module,應(yīng)用了com.android.application 插件。
如果是一個(gè) android library,那么這里的是 apply plugin: 'com.android.library'
-
apply plugin:聲明引用插件的類型。如果是庫的話就加 -
apply from:表示引用其他的配置文件,比如apply from:"config.gradle"
android
compileSdkVersion & buildToolsVersion
-
compileSdkVersion: 基于哪個(gè) SDK 編譯,這里的 Version 是 API LEVEL,是21 -
buildToolsVersion: 基于哪個(gè)構(gòu)建工具版本進(jìn)行構(gòu)建的
defaultConfig
defaultConfig 是默認(rèn)配置,如果沒有其他的配置覆蓋,就會(huì)使用這里的。
常用屬性:
applicationId配置包名的versionCode版本號(hào)versionName版本名稱minSdkVersionapp能夠運(yùn)行的最小版本-
targetSdkVersion目標(biāo)設(shè)備sdk(向前兼容)一般
minSdkVersion<targetSdkVersion<=compileSdkVersionminiSdkVersion: app能運(yùn)行的最小版本,比如minSdkVersion=18(Android 4.3)那么在低于這個(gè)系統(tǒng)版本的設(shè)備上就會(huì)提示不能安裝或運(yùn)行。
compileSdkVersion: 是我們告訴 Gradle,我們是用哪一版本的Android Sdk去編譯程序的,可以使用這個(gè)版本的 API,比如我們使用的是 7.0 的版本
compileSdkVersion=24,那么我們對于拍照裁剪圖片等功能的操作,就可以使用 FileProvider了。
targetSdkVersion: 系統(tǒng)通過 targetSdkVersion 來保證 Android 的向前兼容性,在 Android4.4 之后的設(shè)備上,系統(tǒng)會(huì)判斷你的 targetSdkVersion 是否小于 19,如果小于的話,那就按照 19 之前的 api 方法,如果大于等于 19,那么就按照之后的 api 方法來走,保證了程序運(yùn)行的一致性。也就是向前兼容性。
defaultConfig{} 還有很多其他的配置,后面介紹
buildTypes
buildTypes 是構(gòu)建類型,常用的有 release 和 debug 兩種,可以在這里面啟用混淆,啟用 zipAlign 以及配置簽名信息等。
buildTypes{} 中的其他配置,后面介紹
dependencies
dependencies 就不屬于 Android 專有的配置了,它定義了該 module 需要依賴的 jar,aar,jcenter庫信息。
apply plugin: 'com.android.application'
android { ... }
dependencies {
// Dependency on a local library module
implementation project(":mylibrary")
// Dependency on local binaries
implementation fileTree(dir: 'libs', include: ['*.jar'])
// Dependency on a remote binary
implementation 'com.example.android:app-magic:12.3'
}
本地項(xiàng)目依賴
implementation project(':mylibrary')
這聲明了對一個(gè)名為“mylibrary”(此名稱必須與在您的 settings.gradle 文件中使用 include: 定義的庫名稱相符)的 Android 庫模塊的依賴關(guān)系。在構(gòu)建您的應(yīng)用時(shí),構(gòu)建系統(tǒng)會(huì)編譯該庫模塊,并將生成的編譯內(nèi)容打包到 APK 中。
本地二進(jìn)制文件依賴項(xiàng)
implementation fileTree(dir: 'libs', include: ['*.jar'])
Gradle 聲明了對項(xiàng)目的 module_name/libs/ 目錄中 JAR 文件的依賴關(guān)系(因?yàn)?Gradle 會(huì)讀取 build.gradle 文件的相對路徑)。
或者,您也可以按如下方式指定各個(gè)文件:
implementation files('libs/foo.jar', 'libs/bar.jar')
遠(yuǎn)程二進(jìn)制文件依賴項(xiàng)
implementation 'com.example.android:app-magic:12.3'
這實(shí)際上是以下代碼的簡寫形式:
implementation group: 'com.example.android', name: 'app-magic', version: '12.3'
這聲明了對“com.example.android”命名空間組內(nèi)的 12.3 版“app-magic”庫的依賴關(guān)系。
注意:此類遠(yuǎn)程依賴項(xiàng)要求您聲明適當(dāng)?shù)?a target="_blank">遠(yuǎn)程代碼庫,Gradle 應(yīng)在其中查找相應(yīng)的庫。如果本地不存在相應(yīng)的庫,那么當(dāng) build 需要它時(shí)(例如,當(dāng)您點(diǎn)擊 Sync Project with Gradle Files 圖標(biāo) [站外圖片上傳中...(image-aa6d99-1607933369919)] 或運(yùn)行 build 時(shí)),Gradle 會(huì)從遠(yuǎn)程站點(diǎn)提取它。
依賴項(xiàng)配置
-
implementation: Gradle 會(huì)將依賴項(xiàng)添加到編譯類路徑和構(gòu)建輸出,依賴不會(huì)暴露給其他模塊 -
api:Gradle 會(huì)將依賴項(xiàng)添加到編譯類路徑和構(gòu)建輸出,依賴會(huì)傳遞給其他依賴該模塊的模塊 -
compileOnly:Gradle 只會(huì)將依賴項(xiàng)添加到編譯類路徑,僅在編譯期依賴,運(yùn)行時(shí)可有可無時(shí)可以使用該配置 -
runtimeOnly:Gradle 只會(huì)將依賴項(xiàng)添加到構(gòu)建輸出,以便在運(yùn)行時(shí)使用。也就是說,不會(huì)將其添加到編譯類路徑。
annotationProcessor:添加注解處理器的庫依賴關(guān)系,需使用該配置將其添加到注解處理器類路徑。注:Kotlin 使用 kapt 聲明注解處理器依賴項(xiàng)
以上配置可以將依賴項(xiàng)應(yīng)用于所有構(gòu)建變體
如果想為特定的構(gòu)建變體源代碼集或測試源代碼集聲明依賴,必須將配置的名稱首字母大寫,并在前面加上構(gòu)建變體或測試源代碼集的名稱作為前綴
dependencies {
freeImplementation 'com.google.firebase:firebase-ads:9.8.0'
}
不過,如果您想為將產(chǎn)品變種和構(gòu)建類型組合在一起的變體添加依賴項(xiàng),就必須在 configurations 代碼塊中初始化配置名稱。以下示例向“freeDebug”構(gòu)建變體添加了 runtimeOnly 依賴項(xiàng)(使用本地二進(jìn)制文件依賴項(xiàng)):
configurations {
// Initializes a placeholder for the freeDebugRuntimeOnly dependency
// configuration.
freeDebugRuntimeOnly {}
}
dependencies {
freeDebugRuntimeOnly fileTree(dir: 'libs', include: ['*.jar'])
}
ext{} 代碼塊 定制項(xiàng)目屬性
在根目錄 build.gradle 中, 可以定制適用于所有模塊的屬性,通過 ext 代碼塊來實(shí)現(xiàn),如:
ext {
compileSdkVersion = 28
minSdkVersion = 18
}
然后在模塊的 build.gradle 配置引用這些屬性,語法為:
rootProject.ext.{屬性名}, 比如:
android {
compileSdkVersion rootProject.ext.compileSdkVersion
buildToolsVersion rootProject.ext.buildToolsVersion
}
構(gòu)建類型 android.buildTypes{}
默認(rèn)情況下,Android Plugin 會(huì)自動(dòng)給項(xiàng)目構(gòu)建 debug 和 release 版本。兩個(gè)版本的區(qū)別在于能否在安全設(shè)備(非 dev)上調(diào)試,以及 APK 如何簽名。debug 使用通過通用的 name/password 對生成的密鑰證書進(jìn)行簽名(為了防止在構(gòu)建過程中出現(xiàn)認(rèn)證請求)。release 在構(gòu)建過程中不進(jìn)行簽名,需要自行簽名。
這些配置是通過 BuildType 對象來完成的。默認(rèn)情況下,debug 和 release 實(shí)例都會(huì)被創(chuàng)建。Android plugin 允許像創(chuàng)建其他 Build Type 一樣自定義這兩種類型。在 buildTypes 的 DSL 容器中進(jìn)行配置:
android {
buildTypes {
debug {
applicationIdSuffix ".debug"
}
jnidebug {
initWith(buildTypes.debug)// 復(fù)制
packageNameSuffix ".jnidebug"
jnidebugBuild true
}
}
}
另外,每個(gè) Build Type 都會(huì)創(chuàng)建一個(gè)新的 assemble<BuildTypeName> 任務(wù)。
例如:assembleDebug。
當(dāng) debug 和 release 構(gòu)建類型被預(yù)創(chuàng)建的時(shí)候,assembleDebug 和 assembleRelease 會(huì)被自動(dòng)創(chuàng)建。
buildType 的屬性和方法
https://developer.android.com/reference/tools/gradle-api
| 屬性 | 描述 |
|---|---|
| applicationIdSuffix | 應(yīng)用 id 后綴 |
| name | build type的名字 |
| versionNameSuffix | 版本名稱后綴 |
| minifyEnabled | 是否混淆 |
| proguardFiles | 混淆文件 |
| signingConfig | 簽名配置 |
| multiDexEnabled | 是否拆成多個(gè)Dex |
| 方法 | 描述 |
|---|---|
buildConfigField(type, name, value) |
添加一個(gè)變量生成 BuildConfig 類 |
consumerProguardFile(proguardFile) |
添加一個(gè)混淆文件進(jìn)arr包 |
consumeProguardFile(proguardFiles) |
添加混淆文件進(jìn)arr包 |
externalNativeBuild(action) |
配置本地的build選項(xiàng) |
initWith(that) |
復(fù)制這個(gè) build 類型的所有屬性 |
proguardFile(proguardFile) |
添加一個(gè)新的混淆配置文件 |
proguradFiles(files) |
添加新的混淆文件 |
resValue(type, name, value) |
添加一個(gè)新的生成資源 |
setProguardFiles(proguardFileIterable) |
設(shè)置一個(gè)混淆配置文件 |
啟用 proguard 混淆
可以為不同的 buildTypes 選擇是否啟用混淆,一般 release 發(fā)布版本是需要啟用混淆的,這樣別人反編譯之后就很難分析你的代碼,而我們自己開發(fā)調(diào)試的時(shí)候是不需要混淆的,所以 debug 不啟用混淆。對 release 啟用混淆的配置如下:
android {
buildTypes {
release {
minifyEnabled true
proguardFile 'proguard.cfg'
}
}
}
-
minifyEnabled為 true 表示啟用混淆 -
proguardFile是混淆使用的配置文件
這里是 module 根目錄下的 proguard.cfg 文件
啟用 zipAlign
這個(gè)也是比較簡單的,同樣也是在 buildTypes 里配置,可以為不用的 buildTypes 選擇時(shí)候開啟zipAlign
android {
buildTypes {
release {
zipAlignEnabled true
}
}
}
配置應(yīng)用的簽名信息 android.signingConfigs{}
在 android.signingConfigs{} 下定義一個(gè)或者多個(gè)簽名信息,在 buildTypes{} 配置使用即可。比如:
android {
signingConfigs {
release {
storeFile file("release.keystore")
keyAlias "tina"
keyPassword "123456"
storePassword "123456"
}
debug {
...
}
}
buildTypes {
release {
signingConfig signingConfigs.release
}
debug {
signingConfig signingConfigs.debug
}
}
}
-
storeFile:簽名證書文件 -
keyAlias:別名 -
keyPassword:key的密碼 -
storePassword:證書的密碼
配好相關(guān)信息即可在 buildTypes 配置使用
多渠道打包 android.productFlavors{}
Android Gradle 給我們提供了productFlavors,讓我們可以對生成的 APK 包進(jìn)行定制,所以就有了多渠道。它支持與 defaultConfig 相同的屬性,這是因?yàn)椋?code>defaultConfig 實(shí)際上屬于ProductFlavor
android {
productFlavors {
dev{
}
google{
}
baidu{
}
}
}
這樣當(dāng)我們運(yùn)行 assembleRelease 的時(shí)候就會(huì)生成 3 個(gè) release 包,分別是 dev、google 以及 baidu。目前看這三個(gè)包除了文件名沒有什么不一樣,此時(shí)還沒有定制,使用的都是 defaultConfig 配置。這里的 flavor 和 defaultConfig 是一樣的,可以自定義其 applicationId、versionCode 以及 versionName等信息,比如區(qū)分不同包名:
android {
defaultConfig{
……
// Specifies a flavor dimension.
flavorDimensions "channel"
}
productFlavors {
dev{
applicationId "cn.tina.demo.dev"
}
google{
applicationId "cn.tina.demo.google"
}
}
}
flavorDimensions(風(fēng)格維度),所有變種都必須屬于一個(gè)指定的變種維度,即一個(gè)產(chǎn)品變種組。必須將所有變種分配給某個(gè)變種維度;否則,您將收到如下所示的構(gòu)建錯(cuò)誤。如果給定的模塊僅指定一個(gè)變種維度,則 Android Gradle 插件會(huì)自動(dòng)將該模塊的所有變種分配給該維度。
Error:All flavors must now belong to a named flavor dimension.
The flavor 'flavor_name' is not assigned to a flavor dimension.
注意:flavor 的命名不能與已存在的 Build Type 或者與 androidTest、test sourceSet 有沖突。
每一個(gè) Build Type 都會(huì)生成新的 APK。Product Flavors 同樣也會(huì)做這些事情:項(xiàng)目的輸出將會(huì)組合所有的 Build Types 和 Product Flavors(如果有定義 Flavor)。
每一種組合(包含 Build Type 和 Product Flavor)就是一個(gè) Build Variant(構(gòu)建變種版本)。
例如,在之前的 Flavor 聲明例子中與默認(rèn)的 debug 和 release 兩個(gè) Build Types 將會(huì)生成 4 個(gè) Build Variants:{dev, google}{debug, release}
- dev - debug
- dev - release
- google - debug
- google - release
項(xiàng)目中如果沒有定義 flavor 同樣也會(huì)有 Build Variants,只是使用的是 default 的 flavor/config,因?yàn)槭菬o名稱的,所以生成的 build variant 列表看起來就跟 Build Type 列表一樣。
多定制的變種版本
擴(kuò)展:flavorDimesions 可以設(shè)置多個(gè),如
android {
defaultConfig{
……
// Specifies a flavor dimension.
flavorDimensions "channel","production"
}
productFlavors {
dev{
dimension "channel"
applicationId "cn.tina.demo.dev"
}
google{
dimension "channel"
applicationId "cn.tina.demo.google"
}
productA{
dimension "production"
}
productB{
dimension "production"
}
}
}
那么可以組合出4種(2*2)種產(chǎn)品,分別有 debug 和 release,一共是8個(gè)變種版本
構(gòu)建和任務(wù)
我們前面提到每一個(gè) Build Type 會(huì)創(chuàng)建自己的 assemble<name> task,但是 Build Variants 是 Build Type 和 Product Flavor 的組合。
當(dāng)使用 Product Flavor 的時(shí)候,將會(huì)創(chuàng)建更多的 assemble-type task。分別是:
assemble<Variant Name>允許直接構(gòu)建一個(gè) variant 版本,例如assembleDevDebug。assemble<Build Type Name>允許構(gòu)建指定 Build Type 的所有 APK,例如assembleDebug將會(huì)構(gòu)建DevDebug和GoogleDebug兩個(gè) variant 版本。assemble<Product Flavor Name>允許構(gòu)建指定 flavor 的所有 APK,例如assembleDev將會(huì)構(gòu)建DevDebug和DevRelease兩個(gè) variant 版本。
另外 assemble task 會(huì)構(gòu)建所有的 variant 版本。
android.defaultConfig
AndroidManifest 里的占位符
AndroidManifest.xml 這是一個(gè)很重要的文件,我們的很多配置都在這里定義。有時(shí)候我們的一些配置信息,比如一個(gè)第三方應(yīng)用的 key,第三方統(tǒng)計(jì)分析的渠道號(hào)等也要在這里進(jìn)行配置。
這里以友盟統(tǒng)計(jì)分析平臺(tái)為例,演示這一功能的使用。在友盟統(tǒng)計(jì)分析中,我們需要根據(jù)渠道進(jìn)行統(tǒng)計(jì),比如 google,百度,應(yīng)用寶等渠道的活躍新增等。
友盟的 SDK 是在 AndroidManifest 里配置一個(gè) name 為 UMENG_CHANNEL 的 meta-data,這樣這個(gè) meta-data 的值就表示這個(gè) apk 是哪個(gè)渠道,我們版本發(fā)布有幾十個(gè)渠道,manifestPlaceholders 允許動(dòng)態(tài)替換在 AndroidManifest 文件里定義的占位符。
<meta-data
android:value="${UMENG_CHANNEL_VALUE}"
android:name="UMENG_CHANNEL"
/>
如上 ${UMENG_CHANNEL_VALUE} 就是一個(gè)占位符,然后我們在 gradle 的 defaultConfig 里這樣定義腳本:
android {
defaultConfig {
manifestPlaceholders = [UMENG_CHANNEL_VALUE: 'dev']
}
}
上面代碼塊的意思就是我們的默認(rèn)配置里 AndroidManifest 的 ${UMENG_CHANNEL_VALUE} 占位符會(huì)被 dev 這個(gè)字符串所替換,也就說默認(rèn)運(yùn)行的版本是一個(gè)開發(fā)版。以此類推,我們其他渠道的版本就可以這樣定義:
android {
productFlavors {
google{
applicationId "org.demo.google"
manifestPlaceholders.put("UMENG_CHANNEL_VALUE",'google')
}
baidu{
applicationId "org.demo.baidu"
manifestPlaceholders.put("UMENG_CHANNEL_VALUE",'baidu')
}
}
}
這樣有多少個(gè)渠道就做多少次這樣的定義,即可完成分渠道統(tǒng)計(jì)。但是如果上百個(gè)渠道,這樣一個(gè)個(gè)寫的確太累,很麻煩,我們繼續(xù)研究,同學(xué)們有沒有發(fā)現(xiàn),我們的渠道名字和我們的 flavorName 一樣,我們用這個(gè) flavorName 作為 UMENG_CHANNEL_VALUE 不就好了嗎,可以批量的替換嗎?當(dāng)然可以,這又體現(xiàn)了我們 Gradle 的強(qiáng)大和靈活之處。
productFlavors.all { flavor ->
manifestPlaceholders.put("UMENG_CHANNEL_VALUE",name)
}
循環(huán)每個(gè) flavor,并把他們的 UMENG_CHANNEL_VALUE 設(shè)置為他們自己的 name 名字。
BuildConfig 自定義常量
public final class BuildConfig {
public static final boolean DEBUG = Boolean.parseBoolean("true");
public static final String BUILD_TYPE = "debug";
}
BuildConfig.java 是 Android Gradle 自動(dòng)生成的一個(gè) java 類文件,無法手動(dòng)編譯,但是可以通過 Gradle 控制,也就是說這里是動(dòng)態(tài)可配置的,這里以生產(chǎn)環(huán)境和測試環(huán)境為例來說明該功能的使用。
我們在開發(fā) App 的時(shí)候免不了要和服務(wù)器進(jìn)行通信,我們的服務(wù)器一般都有生產(chǎn)和測試環(huán)境,當(dāng)我們處理開發(fā)和測試的時(shí)候使用測試環(huán)境進(jìn)行調(diào)試,正式發(fā)布的時(shí)候使用生成環(huán)境。以前的時(shí)候我們通過把不同的配置文件打包進(jìn) APK 中來控制,現(xiàn)在不一樣了,我們有更簡便的方法,這就是 buildConfigField。
android {
defaultConfig {
buildConfigField'String','API_SERVER_URL','"http://test.cn/"'
}
productFlavors {
google{
buildConfigField 'String','API_SERVER_URL','"http://release.cn/"'
}
baidu{
buildConfigField 'String','API_SERVER_URL','"http://release.cn/"'
}
}
}
buildConfigField 一共有3個(gè)參數(shù),
- 第一個(gè)是數(shù)據(jù)類型,是定義的常量值是一個(gè)什么類型,和 Java 的類型是對等的,這里是String。
- 第二個(gè)參數(shù)是常量名,這里是
API_SERVER_URL。 - 第三個(gè)參數(shù)是常量值。如此定義之后,就會(huì)在 BuildConfig.java 中生成一個(gè)常量名為
API_SERVER_URL的常量定義。默認(rèn)配置的生成是:
public final static String API_SERVER_URL = "http://test.cn/"
當(dāng)是 baidu 和 google 渠道的時(shí)候生成的就是 http://release.cn/ 了。這個(gè)常量可以在我們編碼中引用。在我們進(jìn)行打包的時(shí)候會(huì)根據(jù) Gradle 配置動(dòng)態(tài)替換。
我們發(fā)現(xiàn)一般渠道版本都是用來發(fā)布的,肯定用的是生產(chǎn)服務(wù)器,所以我們可以使用批處理來搞定這個(gè)事情,而不用在一個(gè)個(gè)渠道里寫這些配置。
productFlavors.all { flavor ->
buildConfigField 'String','API_SERVER_URL','"http://release.org/"'
}
此外,比如 Gradle 的 resValue,也和 buildConfigField 一樣,只不過控制生成的是資源,比如我們在 android 的 values.xml 定義生成的字符串。可以用它來動(dòng)態(tài)生成我們想要的字符串,比如應(yīng)用的名字,可能一些渠道會(huì)不一樣,這樣就可以很靈活的控制自動(dòng)生成,關(guān)于 resValue 詳細(xì)介紹請參考相關(guān)文檔,這里不再舉例說明。