美文网首页
安卓解决java.lang.NoClassDefFoundErr

安卓解决java.lang.NoClassDefFoundErr

作者: 农田蚂蚁 | 来源:发表于2017-07-11 17:21 被阅读0次

    前段时间在开发一个RN项目时,我是负责安卓原生端组件的开发,由于集成了大量的第三方sdk,项目编译的时候正常,但是运行时却出现了java.lang.NoClassDefFoundError这个错误,并且在不同的安卓版本手机运行表现形式都不一样,在安卓版本5.0以下的手机运行都会报这个错,在5.0及以上的手机运行就正常,不会报错,这就让我非常郁闷了。最后查找了大量的网上资料才发现是安卓打包生成Dex 文件方法数超过了最大值65536的上限引起的,这个时候打包时应该出现下面这个错误:

        UNEXPECTED TOP-LEVEL EXCEPTION:  
        java.lang.IllegalArgumentException: method ID not in [0, 0xffff]: 65536  
        at com.android.dx.merge.DexMerger$6.updateIndex(DexMerger.java:501)  
        at com.android.dx.merge.DexMerger$IdMerger.mergeSorted(DexMerger.java:282)  
        at com.android.dx.merge.DexMerger.mergeMethodIds(DexMerger.java:490)  
        at com.android.dx.merge.DexMerger.mergeDexes(DexMerger.java:167)  
        at com.android.dx.merge.DexMerger.merge(DexMerger.java:188)  
        at com.android.dx.command.dexer.Main.mergeLibraryDexBuffers(Main.java:439)  
        at com.android.dx.command.dexer.Main.runMonoDex(Main.java:287)  
        at com.android.dx.command.dexer.Main.run(Main.java:230)  
        at com.android.dx.command.dexer.Main.main(Main.java:199)  
        at com.android.dx.command.Main.main(Main.java:103)
    

    目前已发现的两种隐式报错

    但是项目并没有预期的出现这个错误,实际报错为java.lang.NoClassDefFoundError,意思就是找不到某个类,报错信息也没有看到65536关键字(开发中还发现另一种隐式报错java.lang.VerifyError,也是由于65536上限引起的),这种隐式报错导致我解决问题的时候走了很多弯路,后来查找大量的资料才发现安卓Dex文件方法数超过了65536的上限(这种隐式报错真让人很头疼)。

    关于65536限制

    在Android系统中,一个App的所有代码都在一个Dex文件里面。Dex是一个类似Jar的存储了多有Java编译字节码的归档文件。因为android系统使用Dalvik虚拟机,所以需要把使用Java Compiler编译之后的class文件转换成Dalvik能够执行的class文件。这里需要强调的是,Dex和Jar一样是一个归档文件,里面仍然是Java代码对应的字节码文件。当Android系统启动一个应用的时候,有一步是对Dex进行优化,这个过程有一个专门的工具来处理,叫DexOpt。DexOpt的执行过程是在第一次加载Dex文件的时候执行的。这个过程会生成一个ODEX文件,即Optimised Dex。执行ODex的效率会比直接执行Dex文件的效率要高很多。但是在早期的Android系统中,DexOpt有一个问题,也就是这篇文章想要说明并解决的问题。DexOpt会把每一个类的方法id检索起来,存在一个链表结构里面。但是这个链表的长度是用一个short类型来保存的,导致了方法id的数目不能够超过65536个。当一个项目足够大的时候,显然这个方法数的上限是不够的,所以最终就会报java.lang.NoClassDefFoundError。

    解决方案:

    1、修改build.gradle配置:
    defaultConfig {
            ...
            multiDexEnabled true
     }
    
    dependencies {
      compile 'com.android.support:multidex:1.0.1'
    }
    
    2、根据是否要替换 Application类,执行以下操作之一:

    如果您没有重写 Application类,请编辑清单文件,按如下方式设置<application>标记中的android:name:

    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.example.myapp">
        <application
                android:name="android.support.multidex.MultiDexApplication" >
            ...
        </application>
    </manifest>
    

    如果您重写了 Application类,请按如下方式对其进行更改以继承MultiDexApplication(如果可能):

    public class MyApplication extends MultiDexApplication { ... }
    

    或者,如果您重写了 Application类,但无法继承基本类,则可以改为重写 attachBaseContext()方法并调用MultiDex.install(this)

    public class MyApplication extends SomeOtherApplication {
      @Override
      protected void attachBaseContext(Context base) {
         super.attachBaseContext(context);
         Multidex.install(this);
      }
    }
    

    参考资料:https://developer.android.com/studio/build/multidex.html#mdex-gradle

    相关文章

      网友评论

          本文标题:安卓解决java.lang.NoClassDefFoundErr

          本文链接:https://www.haomeiwen.com/subject/cnsmhxtx.html