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