1. 程式人生 > >Android:一個Multidex引發的VerifyError和Class Not Found問題

Android:一個Multidex引發的VerifyError和Class Not Found問題

一個困擾兩天的問題終於解決了,下面記錄一下該問題解決的歷程,希望能對那些遇到類似問題的猿們有些幫助。

問題背景

由於專案要適配 android4.X,而應用需要引用的一個 jar 包的 4.X 版本就只能用 JDK1.6 來編譯,而應用要用 JDK1.7 來編譯,這個情況也為該問題的解決帶來了干擾。
當把編譯好的 jar 包放入應用中,且應用編譯通過,執行時報各種問題:

java.lang.VerifyError
java.lang.NoClassDefFoundError

等等……
網上說的各種解決辦法都試過了也沒用。後來看到一個帖子
http://www.cnblogs.com/successjerry/p/4402962.html


java.lang.VerifyError 的解決辦法,說是 proguard 引起的。但是我編譯的是 debug 版本,根本沒有用 proguard 啊!但實在沒辦法了,我就想編個 release 版本看看我的 proguard 有沒有問題。結果一試上面的兩個報錯都沒有了,但是出現 了另外一個class 找不到的現象,是一個匿名內部類找不到。
其實這個時候應該可以想到的是可能是 proguard 去除了無用的方法,導致應用的方法數在 65535 之內,前面的出問題的幾個類回到了主包,因此前面出現的兩個問題就沒有了。
但是但是隻考慮是 progurad 混淆引起的,通過反編譯 apk 也確實發現匿名內部類建立是變成了 new 1(this)
之類的,但是主要解決方向都在這裡。
後來糾結了兩天,當中也請教了好多人,還是沒有解決。當時想著就先放一放,就把這個出錯的地方註釋掉了,應用可以跑起來了。
後來的一個線索的出現是,當時修改了一下 jar 包,往裡面添加了幾個方法,執行應用的時候又出現類找不到,方法找不到,這是就恍然大悟了,可能是方法超限的問題導致的。
後來看了一下應用的設定,在 build.gradle 裡面確實配置了分包:

multiDexEnabled true

解決辦法

android.support.multidex.MultiDex.install(this);
  1. 如果沒有自定義自己的 Application
    ,那麼在 AndroidManifest.xml 將 APK 的 Application 指定為 MultiDexApplication
  2. 如果自定義了自己的 Application,那麼將自己的 Application 繼承於 MultiDexApplication
  3. 如果不想繼承於 MultiDexApplication,那麼重寫父類 Applicatio 的成員函式attachBaseContext,並且在該成員函式中呼叫 MultiDex.install 介面。

這裡我們採用方法3。
最後,使用了 Multidex 的 APK 執行在 Android5.0 之後的裝置上時,不需要 MultiDex.install 支援。這是因為 Android 5.0 使用的是 ART 虛擬機器,ART 虛擬機器解決了 Dalvik 虛擬機器方法數限制在 65K 的問題。Android 5.0 在安裝一個使用了 MultiDex 的APK時,會收集它的 Main Dex 和 Additional Dex,然後將它們翻譯成Native Code,最終儲存在一個OAT檔案中。因此就不需要 MultiDex.install 了。
MultiDex.install 幹了什麼事情呢?它首先是收集 APK 裡面的 Additional Dex,並且找到 APK 所使用的 PathClassLoader。接下來通過反射得到 PathClassLoader 的成員變數 pathList,這是一個型別為 DexPathList 的物件。DexPathList 裡面又有一個成員變數dexElements,指向的是一個 Element 陣列,該陣列包含了系統主動為 APK 載入的 Dex。再接下來,又通過反射呼叫上述的 DexPathList 物件的成員函式makeDexElements 載入前面找到的 Additional Dex,並且將這些 Additional Dex 增加到它的成員變數dexElements描述的Element陣列中。
Dalvik 虛擬機器在查詢一個Class的時候,會詢問 APK 使用的PathClassLoaderPathClassLoader又詢問它的成員變數 pathList 指向的DexPathList物件。DexPathList又詢問儲存在它的成員變數dexElements描述的一個Element陣列中的 Dex。因此,就可以想象中,一旦 MultiDex.install 呼叫過後,APK 就可以正常使用打包在AdditionalDex中的 Class。

總結

其實在當發生 Debug 報錯而 Release 版本有些報錯就沒有的時候,本來就可以往這方法考慮的,但是當時走錯了方向,一直以為是混淆導致的問題,沒有考慮到minifyEnabled true這個配置在 release中 也生效了。
希望對大家有所幫助。