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


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

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

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



作者:嘮嗑008
鏈接:https://www.jianshu.com/p/dd5d4fda1df8
來源:簡書
簡書著作權歸作者所有,任何形式的轉載都請聯系作者獲得授權並注明出處。

隨着產品功能需求的增加,我們開發的安卓項目不得不入引入越來越多的第三方庫。這些三方庫可能以 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 語法,如:

compile '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 語句,比如:

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

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

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

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

compile() { dep ->
    ['support-v4', 'support-v13', 'design'].each{ module -> dep.exclude module: module }
}
更多細節,參考: ModuleDependency
以上兩種場景算是比較好處理的。還有一種特殊情況,就是不同第三方庫內部出現相同包名相同文件名的 java 類。這種情況出現的概率很低,但是不幸的是我在工作中就遇見過。
當時項目中引入的 友盟統計移動統一認證 的 SDK 出現重復引入問題,執行 Run 操作編譯打包時出現 duplicate entry 錯誤,如圖:

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

 

 

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

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

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

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

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

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

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

可以看到,不僅包名改變,Jar 包中的相關類引用路徑也自動改變。這種 Jar 包重命名的方式雖然能解決重復引入包的問題,但不是長久之計,需要后續持續關注 SDK 的升級替換。
不同 Jar 包包含相同文件(路徑也相同)的情況還有一種,就是 duplicate files 錯誤。多個 Jar 包包含重復的文件。這種情況在網上看到過,出自 StackOverFlow,錯誤提示類似:

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.jar
You can ignore those files in your build.gradle:
    android {
      packagingOptions {
        exclude 'META-INF/license.txt'
      }
    }

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

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 的使用可能會涉及到這個問題。)




免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2026 CODEPRJ.COM