zoukankan      html  css  js  c++  java
  • Android 谷歌热布丁解决Dex超出方法数的限制问题

    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)  

    没错,你的应用中的Dex 文件方法数超过了最大值65536的上限,简单来说,应用爆棚了.

    那么让我们看一下为什么会引起这种错误:

            在Android系统中,一个App的所有代码都在一个Dex文件里面。Dex是一个类似Jar的存储了多有Java编译字节码的归档文件。因为Android系统使用Dalvik虚拟机,所以需要把使用Java Compiler编译之后的class文件转换成Dalvik能够执行的class文件。这里需要强调的是,DexJar一样是一个归档文件,里面仍然是Java代码对应的字节码文件。当Android系统启动一个应用的时候,有一步是对Dex进行优化,这个过程有一个专门的工具来处理,叫DexOptDexOpt的执行过程是在第一次加载Dex文件的时候执行的。这个过程会生成一个ODEX文件,即Optimised Dex。执行ODex的效率会比直接执行Dex文件的效率要高很多。  但是在早期的Android系统中,DexOpt的LinearAlloc存在着限制: Android 2.2和2.3的缓冲区只有5MB,Android 4.x提高到了8MB或16MB。当方法数量过多导致超出缓冲区大小时,会造成dexopt崩溃,导致无法安装. 

            另外由于DEX文件格式限制,一个DEX文件中method个数采用使用原生类型short来索引文件中的方法,也就是最多表达65536个method,field/class的个数也均有此限制。对于DEX文件,则是将工程所需全部class文件合并且压缩到一个DEX文件期间,也就是Android打包的DEX过程中, 单个DEX文件可被引用的方法总数被限制为65536(自己开发以及所引用的Android Framework和第三方类库的代码).

            目前比较常用的方法:(1) 应用插件化,比如使用我正在参与开发的插件化框架 : https://github.com/singwhatiwanna/dynamic-load-apk ,如果有建议或者相关的问题,欢迎到Github上积极参与. (2) 分割Dex,多工程: 把所需要的.class文件或者是Jar文件和一些源码一起编译生成一个Jar文件。然后使用Android SDK提供的dx工具把Jar文件转成Dex文件。(可参考facebook:https://www.facebook.com/notes/facebook-engineering/under-the-hood-dalvik-patch-for-facebook-for-android/10151345597798920,这里边还可以看到在2.3上动态改变LinearAlloc缓冲的解决思路) 这两种方法并不冲突,插件化除了解决应用爆棚,还有很多其他的优点,可以看我之前的文章,不再复述.

            当然,Google看来也意识到了目前应用方法数爆棚的问题, 目前在已经在API 21中提供了通用的解决方案,那就是android-support-multidex.jar. 这个jar包最低可以支持到API 4的版本(Android L及以上版本会默认支持mutidex).

    让我们看一下如何应用android-support-multidex.jar(以下都以在Anroid studio中的使用为例,使用eclipse开发需要安装gradle插件,其他基本上相同):

    首先可以用--multi-dex配置(build.gradle)进行解决,生成的Apk中将包含多个dex文件,比如classes.dex, classes2.dex. 如下,需要对build.gradle进行修改:

    afterEvaluate {  
        tasks.matching {  
            it.name.startsWith('dex')  
        }.each { dx ->  
            if (dx.additionalParameters == null) {  
                dx.additionalParameters = []  
            }  
            dx.additionalParameters += '--multi-dex' // enable multidex  
      
            // optional  
            // dx.additionalParameters += "--main-dex-list=$projectDir/<filename>".toString() // enable the main-dex-list  
        }  
    }  

    但是默认的Dalvik 类加载器只会寻找classes.dex,所以需要将它们进行合并才能使得被识别

    当然,现在有了android.support.multidex.jar的支持,一切都会变得非常简单

    具体集成:
    将如下配置加入工程 build.gradle

    android {  
        defaultConfig {  
            // Enabling multidex support.  
            multiDexEnabled true  
        }  
    }  
    dependencies {  compile 'com.google.android:multidex:0.1'}  

    MultiDex实现原理:

            Apk在运行的时候,有一个dexpathlist,而Multidex的源码中,会根据你的系统版本号对dexpathlist做修改,将所有的dex都添加到dexpathlist中.



    接下来集成有两个步骤:

    一. 从sdkextrasandroidsupportmultidexlibrarylibs 目录将android-support-multidex.jar导入工程中

    二. 如果你的工程中已经含有Application类,那么让它继承android.support.multidex.MultiDexApplication类,

         如果你的Application已经继承了其他类并且不想做改动,那么还有另外一种使用方式,覆写attachBaseContext()方法:

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

    最后给出build.gradle中的完整配置:

    android {  
        compileSdkVersion 21  
        buildToolsVersion "21.1.0"  
      
        defaultConfig {  
            ...  
            minSdkVersion 14  
            targetSdkVersion 21  
            ...  
      
            // Enabling multidex support.  
            multiDexEnabled true  
        }  
        ...  
    }  
      
    dependencies {  
      compile 'com.android.support:multidex:1.0.0'  
    }  

    使用MutiDex的主意事项

    Application 中的静态全局变量会比MutiDex instal()方法优先加载,所以建议避免在Application类中使用静态变量引用main classes.dex文件以外dex文件中的类,可以根据如下所示的方式进行修改:

    @Override  
        public void onCreate() {  
            super.onCreate();  
      
            final Context mContext = this;  
            new Runnable() {  
      
                @Override  
                public void run() {  
                    // put your logic here!  
                    // use the mContext instead of this here  
                }  
            }.run();  
        }  
  • 相关阅读:
    Selenium 面试题总结
    springbatch 给自定义的processor传递JobParameters中设置的参数
    spring batch 相关的九张表的初始化脚本
    springbatch 读取csv文件时 文件中有字符串需要转换为Date类型的字段报错字符串不能转化为Date类型的解决方法
    spring+quartz定时任务配置---MethodInvokingJobDetailFactoryBean
    springmvc+mybatis+html 下将查询数据以excell形式上传到ftp(下)
    springmvc+mybatis+html 下将查询数据以excell形式上传到ftp(上)
    入门 ARM 汇编(二)—— 寻址方式
    系统进程 zygote(二)—— zygote.rc 脚本
    系统进程 zygote(一)—— 概述
  • 原文地址:https://www.cnblogs.com/huozhong/p/6483623.html
Copyright © 2011-2022 走看看