
文章 | Ashesh Bharadwaj
翻譯 | 承香墨影
授權 承香墨影 翻譯、編輯並發布
在 Android Studio 中,Gradle 構建過程對於開發者來說,很大程度上是抽象的。作為一個新的 Android 開發者,我們第一次遇到 Gradle 通常是在 build.gradle 文件中添加一個遠程依賴項。
讓我們看看如何閱讀 Gradle 依賴關系樹,並解決與依賴關系有關的問題。
這是我工作中管理的一個項目,我想將 targetVersion 升級到 27,我也在 Gradle 中,更新了新版的 appcompat-v7 支持庫到最新的依賴版本 27.0.2。在更改之后,同步項目,在 build.gradle 中出現以下錯誤提示:

該錯誤表示我必須使用完全相同版本的支持庫。可是我只在我的 build.gradle 中,使用了這個支持庫。
Android Studio 的這個提示到底是什么意思?
這個錯誤提示中,提及到的 com.android.support:animated-vector-drawable:27.0.2或com.android.support-v4:21.0.3 是在哪里又被引用了?

如果只是應用程序的直接依賴庫,只需要在 build.gradle 中指明依賴就好了,這樣非常的清晰明了。但是實際情況並非如此,這些依賴關系可能會進一步擁有自己的依賴關系,這被稱為 依賴傳遞 。Gradle 需要在應用中包含所有的直接或者間接的依賴。
前面 Android Studio 提示的錯誤信息,正是我們現在討論的這些傳遞性依賴關系導致的。
Gradle 必須解決所有的這些依賴關系。包含使用了哪些庫?如果兩個不同的庫對同一個庫有不同版本的依賴關系,會發生什么問題?
要查看 Gradle 依賴關系樹(Gradle 解析依賴關系的方式),我們可以到位於 Android Studio 底部的 Terminal 選項卡並輸入以下命令:
gradlew app:dependencies
 
        這將顯示所有 構建變體 的依賴關系解析樹。我們可以在上面的命令中添加一個標識來查看特定構建變體的配置。例如 --configuration releaseCompileClasspath 將向我們展示 release 變體的依賴樹。
關於構建變體,建議閱讀官方文檔:
https://developer.android.com/studio/build/build-variants.html
以上是上述命令的輸出:
releaseCompileClasspath - Resolved configuration for compilation for variant: release
+--- com.android.databinding:library:1.3.1
|    +--- com.android.support:support-v4:21.0.3
|    |    \--- com.android.support:support-annotations:21.0.3 -> 27.0.2
|    \--- com.android.databinding:baseLibrary:2.3.0-dev -> 3.0.1
+--- com.android.databinding:baseLibrary:3.0.1
+--- com.android.databinding:adapters:1.3.1
|    +--- com.android.databinding:library:1.3 -> 1.3.1 (*)
|    \--- com.android.databinding:baseLibrary:2.3.0-dev -> 3.0.1
+--- com.android.support.constraint:constraint-layout:1.0.2
|    \--- com.android.support.constraint:constraint-layout-solver:1.0.2
\--- com.android.support:appcompat-v7:27.0.2
     +--- com.android.support:support-annotations:27.0.2
     +--- com.android.support:support-core-utils:27.0.2
     |    +--- com.android.support:support-annotations:27.0.2
     |    \--- com.android.support:support-compat:27.0.2
     |         +--- com.android.support:support-annotations:27.0.2
     |         \--- android.arch.lifecycle:runtime:1.0.3
     |              +--- android.arch.lifecycle:common:1.0.3
     |              \--- android.arch.core:common:1.0.0
     +--- com.android.support:support-fragment:27.0.2
     |    +--- com.android.support:support-compat:27.0.2 (*)
     |    +--- com.android.support:support-core-ui:27.0.2
     |    |    +--- com.android.support:support-annotations:27.0.2
     |    |    \--- com.android.support:support-compat:27.0.2 (*)
     |    +--- com.android.support:support-core-utils:27.0.2 (*)
     |    \--- com.android.support:support-annotations:27.0.2
     +--- com.android.support:support-vector-drawable:27.0.2
     |    +--- com.android.support:support-annotations:27.0.2
     |    \--- com.android.support:support-compat:27.0.2 (*)
     \--- com.android.support:animated-vector-drawable:27.0.2
          +--- com.android.support:support-vector-drawable:27.0.2 (*)
          \--- com.android.support:support-core-ui:27.0.2 (*)
 
         
         
        在查找目的之前,理解 Gradle 依賴關系樹的格式很重要。
先來談談以下三個符號,它們的目的僅用於格式化:
+- - -是依賴分支庫的開始。|標識還是在之前的依賴庫中的依賴,顯示它依賴的庫。\- - -是依賴庫的末尾。
星號(*) 在依賴庫的末尾,意味着該庫的進一步依賴關系不會顯示,因為它們已經列在其他某個子依賴樹中。
最重要的標識是 -> 。
如果 Gradle 發現多個依賴庫都依賴到同一個庫但是不同版本,那么它必須做出選擇。畢竟包含同一個庫的不同版本是沒有意義的。在這種情況下,Gradle 默認選擇該庫的最新版本。
例如:
| + — — com.android.support:support-v4:21.0.3
| | \ — — com.android.support:support-annotations:21.0.3 -> 27.0.2
 
        在上面,Gradle 告訴說,在 support-v4:21.0.3 依賴關系樹中, support-annotations:21.0.3 依賴於更新的版本 support-annotations:27.0.2 ,所以 27.0.2 將被使用。

現在我們知道如何閱讀 Gradle 依賴關系解析樹,我們回到本文的核心問題:所有 com.android.support 庫必須使用完全相同的版本。
所有支持庫都屬於以下組 com.android.support。正如我們在 Gradle 的依賴關系樹中看到的那樣,com.android.support:support-v4:21.0.3 是唯一具有版本 21.0.3 並且未解析到最新版本的支持庫 27.0.2,這就是造成沖突的原因。
如何解決這個問題?有幾種方式,可以做到這一點:
**1、通過 ResolutionStrategy **
通過 ResolutionStrategy 強制指定 Gradle 的版本。
android {
        configurations.all {
        // To resolve the conflict for com.android.databinding:library:1.3.1
        // dependency on support-v4:21.0.3        
        resolutionStrategy.force 'com.android.support:support-v4:27.0.2'
    }
}
 
        關於 ResolutionStrategy 的具體細節,官方文檔中有詳細描述:
https://docs.gradle.org/current/dsl/org.gradle.api.artifacts.ResolutionStrategy.html
2、在 build.gradle 中指明版本
要在 build.gradle 中,明確指定添加的是 com.android.support:support-v4:27.0.2。這將讓Gradle 覆蓋舊版本。
dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'com.android.support.constraint:constraint-layout:1.0.2'
    // To resolve the conflict for com.android.databinding:library:1.3.1
    // dependency on support-v4:21.0.3
    implementation 'com.android.support:support-v4:27.0.2'
    implementation 'com.android.support:appcompat-v7:27.0.2'
}
 
        對我來說,在 build.gradle 中顯式添加依賴關系似乎更加的自然,並且可以留下注釋。當我們再次更新庫來檢查它是否仍然需要顯式添加時,這個注釋將提醒我關注它。
一旦添加並進行項目同步之后,錯誤文本將消失。現在,如果我們再次運行依賴關系命令,我們將看到support-v4:21.0.3解決-> 27.0.2。

大部分時候 Gradle 會正確解決依賴關系。而了解了 Gradle 的依賴關系,我想遇到這樣的問題我們應該更清楚如何去解決它。
我希望這篇文章能讓我們更進一步了解 Gradle 依賴關系樹和解決方案。
今天在公眾號后台回復成長『成長』,將會得到我整理的一些學習資料,也能回復『加群』,一起學習進步。
原文鏈接:
https://proandroiddev.com/android-gradle-and-the-curious-case-of-invisible-dependency-7f1bcc8bb79e
推薦閱讀:
- 漫畫:程序員,你能“管理”好你的產品經理嗎?
 - 官方新出的 Kotlin 擴展庫 KTX
 - 不懂批判性思維,可能正在限制你的程序員生涯
 - Android 開發,遇上 Emoji 頭疼嗎?
 - Andorid 簽名和多渠道打包方案 | VasDolly
 

