使用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版本的兼容性处理的不够好,导致出现了上述问题
当应用程序指定了最小支持的版本号时,应该以这个最小版本号为准,最好适配工作,使用兼容包去处理高低版本之间的差异性问题。