本來是投稿文章,不會源碼分析類在手機端閱讀體驗實在捉急,感興趣還是建議大家導入源碼後,對著學習下。
Android進階——Small源碼分析之啟動流程詳解
http://blog.csdn.net/qq_30379689/article/details/79208290
Android進階——Small源碼分析之跳轉流程詳解
http://blog.csdn.net/qq_30379689/article/details/79221413
Android進階——Small源碼分析之更新流程詳解
http://blog.csdn.net/qq_30379689/article/details/79242653
作者:shangmingchao
連結:
http://blog.csdn.net/fu908323236/article/details/78356344
本文由作者投稿發布。
構建 APK 的過程是個相當複雜的過程,Android 構建系統需要將應用的資源文件和源文件一同打包到最終的 APK 文件中。應用可能會依賴一些外部庫,構建工具要靈活地管理這些依賴的下載、編譯、打包(包括合併、解決衝突、資源優化)等過程。
應用的源碼可能包括 Java 、RenderScript、AIDL 以及 Native 代碼,構建工具需要分別處理這些語言的編譯打包過程,而有些時候我們需要生成不同配置(如不同 CPU 架構、不同 SDK 版本、不同應用商店配置等)的 APK 文件,構建工具就需要根據不同情況編譯打包不同的 APK。
總之,構建工具需要完成從工程資源管理到最終編譯、測試、打包、發布的幾乎所有工作。而 Android Studio 選擇了使用 Gradle,一個高級、靈活、強大的自動構建工具構建 Android 應用,利用 Gradle 和 Android Gradle 插件可以非常靈活高效地構建和測試 Android 應用了:
Gradle和其Android插件可以幫助你自定義以下幾方面的構建配置:
Build types(構建類型)定義了一些構建、打包應用時 Gradle 要用到的屬性,主要用於不同開發階段的配置,如 debug 構建類型要啟用 debug options 並用 debug key 籤名,release 構建類型要刪除無效資源、混淆源碼以及用 release key 籤名
Product flavors(產品風味)定義了你要發布給用戶的不同版本,比如免費版和付費版。你可以在共享重用通用版本功能的時候自定義 product flavors 使用不同的代碼和資源,Product flavors 是可選的所以你必須手動創建
build variant(構建變體)是 build type 和 product flavor 的交叉輸出(如free-debug、free-release、paid-debug、paid-release),Gradle 構建應用要用到這個配置。也就是說添加 build types 或 product flavors 會相應的添加 build variants
你可以在 build variant 配置中指定 manifest 文件中的某個屬性值(如應用名、最小 SDK 版本、target SDK 版本),這個值會覆蓋 manifest 文件中原來的屬性值
構建系統會管理工程用要用到的本地文件系統和遠程倉庫的依賴。
構建系統會讓你指定籤名設置以便在構建時自動給你的 APK 籤名,構建工具默認會使用自動生成的 debug key 給 debug 版本籤名,你也可以生成自己的 debug key 或 release key 使用。
構建系統讓你可以為每個構建變體指定不同的混淆規則文件
構建系統讓你可以為不同屏幕密度或 Application Binary Interface (ABI)的設備生成包含其所需要的資源的 APK 文件,如為 x86 CPU 架構的設備生成只包含該 x86 架構 so 庫的 APK 文件。
而這些構建配置要體現在不同的構建配置文件中,典型的Android應用結構為:
位於工程根目錄的 settings.gradle 文件用於告訴Gradle構建應用時需要包含哪些 module,如 :
對於setting.gradle中也可以寫代碼的,可以參考:
https://kymjs.com/code/2018/02/25/01/
1.2 頂層 Build 文件
位於工程根目錄的 build.gradle 文件用於定義工程所有 module 的構建配置,一般頂層 build 文件使用 buildscript 代碼塊定義 Gradle 的 repositories 和 dependencies,如自動生成的頂層 build 文件:
buildscript {
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.0.1'
}
}
allprojects {
repositories {
google()
jcenter()
}
}
除了這些,你還可以使用 ext 代碼塊在這個頂層 build 文件中定義工程級(工程中所有 modules 共享)的屬性:
buildscript {...}
allprojects {...}
ext {
compileSdkVersion = 26
supportLibVersion = "27.0.2"
...
}
...
每個 module 的 build 文件使用 rootProject.ext.property_name 語法使用這些屬性即可:
android {
compileSdkVersion rootProject.ext.compileSdkVersion
...
}
...
dependencies {
compile "com.android.support:appcompat-v7:${rootProject.ext.supportLibVersion}"
...
}
位於每個 project/module/ 目錄的 build.gradle 文件用於定義該 module 自己的構建配置,同時你也可以重寫頂層 build 文件或 main app manifest 的配置:
apply plugin: 'com.android.application'
android {
compileSdkVersion 26
buildToolsVersion "27.0.3"
defaultConfig {
applicationId 'com.example.myapp'
minSdkVersion 15
targetSdkVersion 26
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
flavorDimensions "tier", "minApi"
productFlavors {
free {
dimension "tier"
...
}
paid {
dimension "tier"
...
}
minApi23 {
dimension "minApi"
...
}
minApi18 {
dimension "minApi"
...
}
}
splits {
density {
enable false
exclude "ldpi", "tvdpi", "xxxhdpi", "400dpi", "560dpi"
}
}
}
dependencies {
implementation project(":lib")
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.android.support:appcompat-v7:27.0.2'
}
1.4 Gradle 屬性文件
位於工程根目錄的 gradle.properties 文件和 local.properties 用來指定 Gradle 構建工具自己的設置。
gradle.properties 文件可以用來配置工程的 Gradle 設置,如 Gradle 守護進程的最大堆棧大小
local.properties 文件用來配置構建系統的本地環境屬性,如 SDK 安裝路徑,由於該文件內容是 Android Studio 自動生成的且與本地開發環境有關,所以你不要更改更不要上傳到版本控制系統中。
Gradle 是專注於靈活性和性能的開源自動構建工具。Gradle 的構建腳本使用 Groovy 或 Kotlin 語言。Gradle 構建工具的優勢在於:
高度可定製 - Gradle 是以最基本的可定製和可擴展的方式模塊化的
更快 - Gradle 通過重用之前執行的輸出、只處理更改的輸入以及並行執行 task 的方式加快構建速度
更強大 - Gradle 支持跨多語言和平臺,是 Android 官方構建工具,支持很多主流 IDE,包括 Android Studio、Eclipse、IntelliJ IDEA、Visual Studio 2017 以及 XCode,將來會支持更多語言和平臺
學習 Gradle 的途徑有很多:
每個月都有由 Gradle 核心工程師主持的 免費在線培訓,你需要提前註冊預約
https://gradle.org/training
Gradle 的 guides 和 reference documentation 也是很好的閱讀材料
https://gradle.org/guides
https://docs.gradle.org/current/userguide/userguide.html
通過 Gradle Newsletter 閱讀Gradle最新期刊
https://newsletter.gradle.com/
通過 Gradle Forum 論壇獲取幫助,其他成員或核心開發者會回答你的問題
https://discuss.gradle.org/
還可以通過 Command-line completion 學習 bash 和 zsh 命令行
https://git.io/gradle-completion
依賴管理(Dependency management)是每個構建系統的關鍵特徵,Gradle 提供了一個既容易理解又其他依賴方法兼容的一流依賴管理系統,如果你熟悉 Maven 或 Ivy 用法,那麼你肯定樂於學習 Gradle,因為 Gradle 的依賴管理和兩者差不多但比兩者更加靈活。
Gradle 依賴管理的優勢包括:
傳遞依賴管理 - Gradle 讓你可以完全控制工程的依賴樹
支持非託管依賴 - 如果你只依賴版本控制系統或共享磁碟中的單個文件,Gradle 提供了強大的功能支持這種依賴
支持個性化依賴定義 - Gradle 的 Module Dependencies 讓你可以在構建腳本中描述依賴層級
為依賴解析提供完全可定製的方法 - Gradle 讓你可以自定義依賴解析規則以便讓依賴可以方便地替換
完全兼容Maven和Ivy - 如果你已經定義了 Maven POM 或 Ivy 文件,Gradle 可以通過相應的構建工具無縫集成
可以與已存在的依賴管理系統集成 - Gradle 完全兼容 Maven 和 Ivy 倉庫,所以如果你使用 Archiva、Nexus 或 Artifactory,Gradle 可以100%兼容所有的倉庫格式
Java Library插件 繼承自 Java插件,但 Java Library 插件與 Java 插件最主要的不同是 Java Library 插件引入了將 API 暴露給消費者(使用者)的概念,一個 library 就是一個用來供其他組件(component)消費的 Java 組件。
Java Library 插件暴露了兩個用於聲明依賴的 Configuration(依賴配置): api 和 implementation。出現在 api 依賴配置中的依賴將會傳遞性地暴露給該 library 的消費者,並會出現在其消費者的編譯 classpath 中。而出現在 implementation 依賴配置中的依賴將不會暴露給消費者,也就不會洩漏到消費者的編譯 classpath 中。因此,api 依賴配置應該用來聲明library API 使用的依賴,而 implementation 依賴配置應該用來聲明組件內部的依賴。implementation 依賴配置有幾個明顯的優勢:
依賴不會洩漏到消費者的編譯 classpath 中,所以你也就不會無意中依賴一個傳遞依賴了
由於 classpath 大小的減少編譯也會更快
當 implementation 的依賴改變時,消費者不需要重新編譯,要重新編譯的很少
更清潔地發布,當結合新的 maven-publish 插件使用時,Java librariy 會生成一個 POM 文件來精確地區分編譯這個 librariy 需要的東西和運行這個 librariy 需要的東西
那到底什麼時候使用 API 依賴什麼時候使用 Implementation 依賴呢?
這裡有幾個簡單的規則:
一個 API 是 library binary 接口暴露的類型,通常被稱為 ABI (Application Binary Interface),這包括但不限於:
相反,下面列表重要到的所有類型都與 ABI 無關,因此應該使用 implementation 依賴:
只用在方法體內的類型
只出現在 private 成員的類型
只出現在內部類中的類型
例如
import org.apache.commons.httpclient.*;
import org.apache.commons.httpclient.methods.*;
import org.apache.commons.lang3.exception.ExceptionUtils;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
public class HttpClientWrapper {
private final HttpClient client;
public HttpClientWrapper(HttpClient client) {
this.client = client;
}
public byte[] doRawGet(String url) {
GetMethod method = new GetMethod(url);
try {
int statusCode = doGet(method);
return method.getResponseBody();
} catch (Exception e) {
ExceptionUtils.rethrow(e);
} finally {
method.releaseConnection();
}
return null;
}
private int doGet(GetMethod method) throws Exception {
int statusCode = client.executeMethod(method);
if (statusCode != HttpStatus.SC_OK) {
System.err.println("Method failed: " + method.getStatusLine());
}
return statusCode;
}
}
其中,public 構造器 HttpClientWrapper 使用了 HttpClient 參數暴露給了使用者,所以屬於 API 依賴。而 ExceptionUtils 只在方法體中出現了,所以屬於 implementation 依賴。
所以 build 文件這樣寫:
dependencies {
api 'commons-httpclient:commons-httpclient:3.1'
implementation 'org.apache.commons:commons-lang3:3.5'
}
因此,應該優先選擇使用 implementation 依賴:缺少一些類型將會直接導致消費者的編譯錯誤,可以通過移除這些類型或改成 API 依賴解決。
compileOnly 依賴配置會告訴 Gradle 將依賴只添加到編譯 classpath 中(不會添加到構建輸出中),在你創建一個 Android library module 且在編譯時需要這個依賴時使用 compileOnly 是個很好的選擇。但這並不能保證運行時良好,也就是說,如果你使用這個配置,那麼你的 library module 必須包含一個運行時條件去檢查依賴是否可用,在不可用的時候仍然可以優雅地改變他的行為來正常工作,這有助於減少最終 APK 的大小(通過不添加不重要的transient依賴)。
runtimeOnly 依賴配置告訴 Gradle 將依賴只添加到構建輸出中,只在運行時使用,也就是說這個依賴不添加到編譯 classpath 中。
此外,debugImplementation 會使依賴僅在 module 的 debug 變體中可用,而如 testImplementation、androidTestImplementation 等依賴配置可以更好地處理測試相關依賴。
現在的軟體工程很少單獨地構建代碼,因為現在的工程通常為了重用已存在且久經考驗的功能而引入外部庫,因此被稱為 binary dependencies。Gradle 會解析 binary 依賴然後從專門的遠程倉庫中下載並存到 cache 中以避免不必要的網絡請求:
每個 artifact 在倉庫中的 coordinate 都會包含 groupId 、 artifactId 和 version 三個元素,如在一個使用 Spring 框架的 Java 工程中添加一個編譯時依賴:
apply plugin: 'java-library'
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework:spring-web:5.0.2.RELEASE'
}
Gradle 會從 Maven中央倉庫 https://search.maven.org/ 解析並下載這個依賴(包括它的傳遞依賴),然後使用它去編譯 Java 源碼,其中的 version 屬性是指定了具體版本,表明總是使用這個具體的依賴不再更改。
當然,如果你總是想使用最新版本的 binary 依賴,你可以使用動態的 version,Gradle 默認會緩存 24 小時:
implementation 'org.springframework:spring-web:5.+'
有些情況開發團隊在完全完成新版本的開發之前為了讓使用者能體驗最新的功能特色,會提供一個 changing version,在 Maven 倉庫中 changing version 通常被稱作 snapshot version,而 snapshot version會包含-SNAPSHOT後綴,如:
implementation 'org.springframework:spring-web:5.0.3.BUILD-SNAPSHOT'
工程有時候不會依賴 binary 倉庫中的庫,而是把依賴放在共享磁碟或者版本控制系統的工程源碼中(JFrog Artifactory 或 Sonatype Nexus 可以存儲解析這種外部依賴),這種依賴被稱為 file dependencies ,因為它們是以不涉及任何 metadata(如傳遞依賴、作者)的文件形式存在的。如我們添加來自 ant、libs 和 tools 目錄的文件依賴:
configurations {
antContrib
externalLibs
deploymentTools
}
dependencies {
antContrib files('ant/antcontrib.jar')
externalLibs files('libs/commons-lang.jar', 'libs/log4j.jar')
deploymentTools fileTree(dir: 'tools', include: '*.exe')
}
現在的工程通常把組件獨立成 module 以提高可維護性及防止強耦合,這些 module 可以定義相互依賴以重用代碼,而 Gradle 可以管理這些 module 間的依賴。由於每個 module 都表現成一個 Gradle project,這種依賴被稱為 project dependencies 。在運行時,Gradle 構建會自動確保工程的依賴以正確的順序構建並添加到 classpath 中編譯。
project(':web-service') {
dependencies {
implementation project(':utils')
implementation project(':api')
}
}
強制所有的 android support libraries 使用相同的版本:
configurations.all {
resolutionStrategy {
eachDependency { details ->
if (details.requested.group == 'com.android.support' &&
details.requested.name != 'multidex' &&
details.requested.name != 'multidex-instrumentation') {
details.useVersion supportLibVersion
}
}
}
}
更改生成的 APK 文件名:
android.applicationVariants.all { variant ->
variant.outputs.all {
outputFileName = "${variant.name}-${variant.versionName}.apk"
}
}
如果開啟了 Multidex 後在 Android 5.0 以下設備上出現了 java.lang.NoClassDefFoundError 異常,可能是由於構建工具沒能把某些依賴庫代碼放進主 dex 文件中,這時就需要手動指定還有哪些要放入主 dex 文件中的類。在構建類型中指定 multiDexKeepFile 或 multiDexKeepProguard 屬性即可:
在 build 文件同級目錄新建 multidex-config.txt 文件,文件的每一行為類的全限定名,如:
com/example/MyClass.class
com/example/MyOtherClass.class
android {
buildTypes {
release {
multiDexKeepFile file('multidex-config.txt')
...
}
}
}
或者新建 multidex-config.pro 文件,使用 Proguard 語法指定放入主 dex 文件中的類,如:
-keep class com.example.** { *; }
android {
buildTypes {
release {
multiDexKeepProguard file('multidex-config.pro')
...
}
}
}
參考
Configure Your Build
https://developer.android.com/studio/build/index.html#build-process
Gradle User guides
https://gradle.org/guides
Gradle reference documentation
https://docs.gradle.org/current/userguide/userguide.html
最後推薦一下我做的網站,玩Android: wanandroid.com ,包含詳盡的知識體系、好用的工具,還有本公眾號文章合集,歡迎體驗和收藏!
推薦閱讀:
堅持寫技術博客
使用RxJava的最佳開發場景
如果你想要跟大家分享你的文章,歡迎投稿~