使用Retrofit 2時踩了個坑,自己封裝的Retrofit工具在4.4的手機上跑崩了,測試了下機型,發現在Android 5.x以上的手機就不會出現這樣的情況,而在Android 4.x手機上這個問題是必現的
錯誤信息
java.lang.NoClassDefFoundError: retrofit2.Retrofit$Builder
一看到這個錯誤信息,馬上聯想到是否是類缺失了,但是這裏指向的是Retrofit2.Retrofit$Builder,導入第三方類庫怎麼還會缺少類呢,而且如果是類缺失,那麼在程序編譯期間就會報錯的,仔細一看是自己眼花了,NoClassDefFoundError
錯誤和ClassNotFoundException
經常容易被搞混,這兩個錯誤類型雖然都指的是找不到類,但是差異還是挺大的
ClassNotFoundExceptio
這個錯誤比較好解決,就是程序找不到指定的class類,就會出現這個問題,通常發生在程序的編譯期間
NoClassDefFoundError
這個錯誤就比較難找了,在程序正常編譯後還報這個未找到類的異常,也就是說這個class類在程序的編譯階段並沒有缺失,但是在程序運行階段,無法把它正常的從內存中加載出來,我們通常使用new
方法來實例化一個類,如果這個類在程序運行的時候“失蹤”了,那麼就會拋出這個異常。
class A {
public String getMethod(){
return "method";
}
}
public class B {
public static void main(String[] args)
{
A a = new A();
System.out.println(a.getMethod());
}
}
就比如說,我們有兩個類A和B,B調用A中的方法,我們編譯好後,實際上是生成了兩個.class文件,這個時候如果我們把A.class文件刪掉,再運行程序,那麼就會出現NoClassDefFoundError
異常了。
解決方法
瞭解了異常產生的原因,就開始對症下藥了,居然在編譯期間沒有出錯,那麼說明類文件並沒有缺失,而是在編譯完成後,運行程序時無法正常的獲取到Retrofit$Builder
類。
在Android中,我們經常會使用到分包方法,即multiDexEnabled
,尤其是當我們的程序應用了大量第三方框架的時候,這個參數就更重要了,因爲Android中編譯的單個.dex文件最多支持65536個方法,而如果大於這個方法數,就需要使用到multiDexEnabled
方法對Dalvik可執行文件.dex進行分包了,而在分包的時候就可能把Retrofit需要使用到的方法分到不同的dex文件中,所以問題應該就出在multiDexEnabled
的使用上了。
android {
...
defaultConfig {
...
multiDexEnabled true
}
...
}
這個分包方法,是在Android 5.0之後提供的,如果程序運行在5.0以上的手機,版本設置爲minSdkVersion 21
以上的應用來說,以上這麼配置的方式就可以了
而我在程序中指定的最低支持到的版本是API 19,所以爲了兼容Android 5.0以下的手機,需要在build.gradle
添加個依賴庫com.android.support:multidex:1.0.1
android {
...
defaultConfig {
...
multiDexEnabled true
}
...
}
dependencies {
compile 'com.android.support:multidex:1.0.1'
}
同時,對於被我們替換的Application
類,需要繼承的是MultiDexApplication
,而不是Application
public class AppApplication extends MultiDexApplication {...}
通過以上的正確方法配置好了之後,再次運行,就沒有再報NoClassDefFoundError
錯誤了。
- 如果你不想繼承
MultiDexApplication
類,而需要繼承其它類型的Application
類,那麼可以通過重寫其attachBaseContext
方法,在其中
手動調用 Dalvik 可執行文件分包方法MultiDex.install()
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(context);
MultiDex.install(this);
}
- 如果沒有替換
Application
類,那麼就需要在配置文件AndroidManifest.xml
中指定<application>
標籤的name爲android.support.multidex.MultiDexApplication
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.text">
<application
android:name="android.support.multidex.MultiDexApplication" >
...
</application>
</manifest>
總結
事實上這個錯誤並不是由於Retrofit本身造成的,而是錯誤的使用multiDexEnabled
方法造成的,對於多Android版本的兼容性處理的不夠好,導致出現了上述問題
當應用程序指定了最小支持的版本號時,應該以這個最小版本號爲準,最好適配工作,使用兼容包去處理高低版本之間的差異性問題。