相依性問題發生在共用套件或程式庫上,其中多個其他套件具有相依性,但相依於不同和不相容的共用套件版本。Gradle 讓 Android 開發人員能夠輕鬆應對,只需在 build.gradle 中新增一個相依性,就可以無縫地在建置中加入必要的程式庫。但是,當兩個相依性相依於不同版本的相同程式庫時,會發生什麼狀況呢?
如果兩個相依性屬於相同設定(即應用程式設定),Gradle 將能自動解決相依性問題。例如,Audience Network Android SDK 相依於「exoplayer」程式庫。如果您需要新增不同的「exoplayer」程式庫版本,Gradle 將選擇加入較新的版本。但是,如果兩個相依性屬於不同設定(即應用程式和測試),Gradle 將擲回錯誤。下面我們將介紹不同的衝突和解決方案。
繼續操作前,請務必詳閱 Audience Network 新手指南與 Android 新手指南。
請思考下列範例。以下程式碼中的兩個相依性皆保持相同的設定,並且對程式庫「org.hamcrest:hamcrest-core」具有內部相依性。由於這兩個相依性都在內部使用不同版本的相同程式庫,因此系統會在建置中加入最新版本。Gradle 同步的輸出清楚表示,會在最終建置中自動將 hamcrest 程式庫版本從 1.1 版升級到 1.3 版。
// Depends on version 1.3 of org.hamcrest:hamcrest-core androidTestImplementation 'junit:junit:4.12' // Depends on version 1.1 of org.hamcrest:hamcrest-core androidTestImplementation 'org.mockito:mockito-core:1.10.19'
如果兩個相依性屬於不同設定(即應用程式和測試設定),Gradle 將擲回錯誤。請考慮下列程式碼片段。第一個相依性屬於應用程式設定,而第二個相依性屬於測試設定。因此,在建置專案時,將發生例外狀況而失敗。
執行檢測測試時,主 APK 和測試 APK 會共用相同的類別路徑。如果主 APK 和測試 APK 使用相同的程式庫(例如 Guava),但版本不同,Gradle 建置將失敗。如果 Gradle 沒有注意到前述問題,應用程式在測試期間和正常執行期間的行為可能會有所不同(包括其中一種情況是當機)。
// Depends on version 1.3 of org.hamcrest:hamcrest-core implementation 'junit:junit:4.12' // Depends on version 1.1 of org.hamcrest:hamcrest-core androidTestImplementation 'org.mockito:mockito-core:1.10.19'
此方法是最簡單的方法,但僅限於相同設定中的相依性。例如,您的模組明確相依於特定版本的 ExoPlayer。但是,Audience Network SDK 已包含另一個版本的 ExoPlayer。根據預設,系統會在建置中加入最新版本。透過以下程式碼片段,Gradle 同步的輸出清楚表示,會在最終建置中自動將 ExoPlayer 程式庫版本從 r2.4.2 版升級到 2.7.3 版。
implementation 'com.google.android.exoplayer:exoplayer-core:2.7.3' implementation 'com.google.android.exoplayer:exoplayer-dash:2.7.3' ... // audience-network-sdk depends on exoplayer-core:r2.4.2 and exoplayer-dash:r2.4.2 implementation 'com.facebook.android:audience-network-sdk:4.28.1'
多數情況下,開發人員可能希望控制加入建置的最終程式庫版本。您可以設定 build.gradle 來這麼做。例如,如果開發人員偏好舊的 ExoPlayer r2.4.0 版而不是 Audience Network SDK 中的 r2.4.2 版,可以在宣告「audience-network-sdk」相依性時排除該模組。排除標籤也適用於不同設定中的相依性。
在實際專案中,將有許多相依性具有不同版本的相同程式庫。在此情況下,必須給予每個相依性排除標籤,如此才能加入該程式庫的預期版本。
情境 1:相同設定中的相依性implementation 'com.google.android.exoplayer:exoplayer-core:r2.4.0' implementation 'com.google.android.exoplayer:exoplayer-dash:r2.4.0' ... // audience-network-sdk depends on exoplayer-core:r2.4.2 and exoplayer-dash:r2.4.2 implementation ('com.facebook.android:audience-network-sdk:4.28.1') { exclude group: 'com.google.android.exoplayer', module:'exoplayer-core' exclude group: 'com.google.android.exoplayer', module:'exoplayer-dash' }情境 2:不同設定中的相依性
// Depends on version 1.3 of org.hamcrest:hamcrest-core implementation 'junit:junit:4.12' // Depends on version 1.1 of org.hamcrest:hamcrest-core androidTestImplementation ('org.mockito:mockito-core:1.10.19'){ exclude group: 'org.hamcrest', module:'hamcrest-core' }
此方法可以更簡潔解決不同設定相依性的衝突問題。在此情況下,我們需要明確指出程式庫的版本,其為我們希望在任何設定之一的最終建置中加入的版本。
此方法可以更簡潔解決衝突問題,但缺點是,在更新實際的相依性(例如 junit 和 mockito)時,開發人員也需要更新衝突的程式庫。
// Depends on version 1.3 of org.hamcrest:hamcrest-core implementation 'junit:junit:4.12' // Depends on version 1.1 of org.hamcrest:hamcrest-core androidTestImplementation 'org.mockito:mockito-core:1.10.19' // Explictly mention that include version 1.3 of org.hamcrest:hamcrest-core androidTestCompile 'org.hamcrest:hamcrest-core:1.3'
這是另一種解決衝突的方法,此方法不宣告單一設定,而是在所有設定下強制執行。在以下範例中,您可將自己的「resolutionStrategy」新增至模組層級 build.gradle 中,以強制加入特定的套件版本,無論為相同或不同設定的相依性。
使用此方法時應注意。在我們的第一個範例中,如果 audience-network-sdk 已更新,且這些程式庫更新 exoplayer-core 和 exoplayer-dash 程式庫的版本,我們仍將強制使用較新的版本。雖然此情境適用於第二種解決方案,但在此方法中,我們將對所有設定而非單一設定強制使用相依性版本。
情境 1:相同設定中的相依性android { configurations.all { resolutionStrategy.force 'com.google.android.exoplayer:exoplayer-core:r2.4.0' resolutionStrategy.force 'com.google.android.exoplayer:exoplayer-dash:r2.4.0' } }情境 2:不同設定中的相依性
android { configurations.all { resolutionStrategy.force 'org.hamcrest:hamcrest-core:1.1' } }