Android:一个Multidex引发的VerifyError和Class Not Found问题

来源:互联网 发布:双色球红球246计算法 编辑:程序博客网 时间:2024/06/16 12:49

一个困扰两天的问题终于解决了,下面记录一下该问题解决的历程,希望能对那些遇到类似问题的猿们有些帮助。

问题背景

由于项目要适配android4.X,而应用需要引用的一个jar包的4.X版本就只能用JDK1.6来编译,而应用要用JDK1.7来编译,这个情况也为该问题的解决带来了干扰。
当把编译好的jar包放入应用中,且应用编译通过,运行时报各种问题:

java.lang.VerifyErrorjava.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

解决办法

后来在网上查资料,发现
http://blog.csdn.net/t12x3456/article/details/40837287
可能需要在Application类的attachBaseContext方法中加上:

android.support.multidex.MultiDex.install(this);

一试,果然没有报错了。

http://weibo.com/p/1001603884849646685222

http://blog.csdn.net/qq2603825424/article/details/48311961
中有介绍:
使用了Multidex的APK运行在Android5.0之前的设备上时,还需要配合support库里面的MultiDex.install接口才行。有三种方法使用MultiDex.install接口:

  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使用的PathClassLoader。PathClassLoader又询问它的成员变量pathList指向的DexPathList对象。DexPathList又询问保存在它的成员变量dexElements描述的一个Element数组中的Dex。因此,就可以想象中,一旦MultiDex.install调用过后,APK就可以正常使用打包在AdditionalDex中的Class。

总结

其实在当发生Debug报错而Release版本有些报错就没有的时候,本来就可以往这方法考虑的,但是当时走错了方向,一直以为是混淆导致的问题,没有考虑到minifyEnabled true这个配置在release中也生效了。
希望对大家有所帮助。

0 0
原创粉丝点击