有關 Android Studio 重復引入包的問題和解決方案

有關 Android Studio 重復引入包的問題和解決方案。

隨著產品功能需求的增加,我們開發的安卓項目不得不入引入越來越多的第三方庫。這些三方庫可能以 Jar 包的形式放置在 libs 目錄下,可能以 Gradle 遠程依賴的形式下載引入,也可能是以 Library Module 的形式放置在工程目錄下,等等。

隨之而來的問題是,復雜的依賴關系很可能導致重復引入包的問題。比較常見的使用場景就是 support-v4 包的重復引入。這樣就會導致,執行 Run 操作打包生成 Apk 文件時出現類似這樣的 DexException 錯誤提示,導致編譯失敗:

Error:Execution failed for task ':app:transformClassesWithDexForDebug'.> com.android.build.api.transform.TransformException: com.android.ide.common.process.ProcessException: java.util.concurrent.ExecutionException: com.android.dex.DexException: Multiple dex files define Landroid/support/v4/app/ActivityCompatHoneycomb;“` 有多種使用場景會出現這種問題。根據解決方案的不同,大體上可以分為兩種:本地 Jar 包重復嵌入和 Gradle 遠程重復依賴。第一種,比較好理解。比如 app module 與 library module 各自 libs 目錄中嵌入瞭相同的 Jar 包。這種情況也比較好解決,隻需要將 app module 下的重復 jar 包刪除即可。第二種,稍微復雜一點。比如對於 Gradle 遠程依賴的兩個第三方庫,他們內部同時依賴相同的另一個輔助第三方庫。這個時候我們就沒辦法像第一種情況那樣手動刪除本地文件。好在 Gradle 插件提供瞭相應的解決方案,即使用 exclude 語法,如:“`xmlcompile 'com.yifeng.example:example-1:1.0'compile ('com.yifeng.example:example-2:1.0') { exclude group: 'com.android.support', module: 'support-v4'}

如例子中所示,遠程依賴的第三方庫 example-1 與 example-2 內部同時引入 support-v4 包,那麼隻需要在其中一個的引入地方添加 exclude 語句,根據 group 和 module 過濾規則,將相同引入的 v4 包剔除在外即可。

當需要在一個依賴中去除多個遞歸依賴項時,可以使用多條 exclude 語句,比如:

1234

compile ('com.wdullaer:materialdatetimepicker:3.2.2') { exclude group: 'com.android.support', module: 'support-v4' exclude group: 'com.android.support', module: 'design'}

還有一種更簡單的寫法,使用 Groove 語言的循環語句,模板如下(同樣適用於 module 規則):

123

compile() { dep -> [group1, group2].each{ group -> dep.exclude group: group }}

對於上面的例子,便可以改造成:

123

compile() { dep -> ['support-v4', 'support-v13', 'design'].each{ module -> dep.exclude module: module }}

更多細節,參考:ModuleDependency)。

以上兩種場景算是比較好處理的。還有一種特殊情況,就是不同第三方庫內部出現相同包名相同文件名的 java 類。這種情況出現的概率很低,但是不幸的是我在工作中就遇見過。

當時項目中引入的友盟統計和移動統一認證的 SDK 出現重復引入問題,執行 Run 操作編譯打包時出現 duplicate entry 錯誤,如圖:

通過錯誤提示,很容易就找到錯誤出處,我們看下兩個 SDK 中的 jar 包源碼:

友盟統計 SDK

移動統一認證 SDK

雖然相同包名相同類名的文件在不同 SDK 中出現的概率極低,但是一旦出現,處理起來就比較棘手。最好的解決方案就是聯系提供 SDK 的技術人員反映問題,讓其通過修改源碼重新打包一個新的 Jar 包。

還有一個解決辦法就是,重新命名 Jar 包裡的包名或者文件名。網上也有一個工具:jarjar.jar,可以幫助我們重命名包名和文件名,以及 Jar 包中的相關代碼引用路徑。參考地址如下:

GitHub 源碼:https://github.com/shevek/jarjar

Jar 文件下載:https://code.google.com/archive/p/jarjar/downloads

該工具提供有多種使用方式,最簡單實用的就是通過命令行使用。舉個例子,打開命令行工具,執行:

1

java -jar jarjar-1.4.jar process rule.txt example.jar example_output.jar

其中,rule.txt 是包含重命名規則的文件,內容如下:

1

rule p.rn.asm.** com.yifeng.example.@1

我們看一下重命名前後 Jar 包內容的代碼對比圖:

可以看到,不僅包名改變,Jar 包中的相關類引用路徑也自動改變。這種 Jar 包重命名的方式雖然能解決重復引入包的問題,但不是長久之計,需要後續持續關註 SDK 的升級替換。

不同 Jar 包包含相同文件(路徑也相同)的情況還有一種,就是 duplicate files 錯誤。多個 Jar 包包含重復的文件。這種情況在網上看到過,出自StackOverFlow,錯誤提示類似:

12345678910

Error:duplicate files during packaging of APK E:\Code\iDoc\app\build\outputs\apk\app-debug-unaligned.apk Path in archive: META-INF/license.txt Origin 1: E:\Code\iDoc\app\libs\spring-core-3.1.0.RELEASE.jar Origin 2: E:\Code\iDoc\app\libs\spring-web-3.1.0.RELEASE.jarYou can ignore those files in your build.gradle: android { packagingOptions { exclude 'META-INF/license.txt' } }

可以看到,解決方案已經在錯誤提示中有給出,使用 packagingOptions 配置的 exclude 語句刪除重復文件即可,比如:

1234567

packagingOptions { exclude 'META-INF/DEPENDENCIES' exclude 'META-INF/NOTICE' exclude 'META-INF/LICENSE' exclude 'META-INF/LICENSE.txt' exclude 'META-INF/NOTICE.txt' }

最後再補充一點,關於 Gradle 依賴的 Scope 問題。通常依賴某個第三方庫我們使用的都是compile關鍵字,實際上,還有一個Provided關鍵字偶爾也會用到。

打開 Project Structure,查看 Modules 的 Dependencies 內容時,可以看到每一個依賴項的右邊有個 Scope 選項:

compile 表示依賴的第三方庫在工程編譯階段、測試階段和 Apk 運行階段都需要使用到;而 provided 表示該第三方庫僅僅是在編譯階段和測試階段使用到,不會出現在運行階段,即表示不會打包到最終的 apk 文件中去,目標運行環境已經包含有該第三方庫。(貌似 Dagger2 的使用可能會涉及到這個問題。)

You May Also Like