一个诡异的 Android 大坑

作者: song4 | 来源:发表于2015-09-08 00:00 被阅读1241次

    编译 Android 工程的时候,意外遇到了这样一个错误:

    com.android.dex.DexIndexOverflowException: Cannot merge new index 66739 into a non-jumbo instruction!
    

    通过谷歌搜索这个问题,发现 StackOverflow 上面已经有人遇到过相同的问题了:Application too big? Unable to execute dex: Cannot merge new index into a non-jumbo instruction

    大家给出的解释是,一个 dex 文件中至多容许存在 64K 个方法,超过这一限制,编译器就会报错。

    至于为什么 Android 中会有这样一个诡异的数目限制,我猜测是因为 dalvik 中的某个计数器使用了 short 类型,因为 64K 刚好等于 65536。检索了一番 dalvik 的源码,发现确实如此,下面是埋下了这个大坑的代码:

    // dalvik/dx/src/com/android/dx/merge/IndexMap.java
    public IndexMap(Dex target, TableOfContents tableOfContents) {
        ...
        public final short[] typeIds;
        public final short[] protoIds;
        public final short[] fieldIds;
        public final short[] methodIds;
        ...
    }
    

    从代码中可以看出,不只是 methodIds 不能超过 65536 个,typeIdsprotoIds 以及 fieldIds 也都是如此。想必 dalvik 的开发者当初写下这几行代码的时候,心中是这么考虑的:六万五千多个方法还不够你们用的吗?

    开发者在设计新功能或者新协议的时候,往往显得过于保守与短视。比如,IPv4 的设计者们显然并没有预料到我们今天会遭遇 IP 地址耗尽的危机。

    现在,同样的问题出现在了 Android 开发者的面前。随着应用程序功能的日益复杂,源码的规模正在逐渐增大,势必有一天,代码中方法的数量终将超出限制。最彻底的解决办法自然是升级 dalvik 的源代码,把计数器的计数范围扩大。然而,由于当前版本的 dalvik 已经广泛运行在了大量的安卓设备中(全世界安卓设备的激活数已经超过了 10 亿台),这种毫无向前兼容性的解决办法显然不可取。

    那么,作为开发者,该怎样避免这个问题呢?Android 的官方开发者网站上面有一篇专门讨论这个主题的文章: Building Apps with Over 65K Methods

    总结来说,有两种可选方案:

    一种办法是减小程序的规模,这可以通过刨除无用代码以及减少项目的依赖来达成。不过这终究只是权宜之计,只能拖延时间,并不能阻止末日的到来。

    另一种办法是把编译生成的字节码文件分割成为两个或者更多 dex 文件,以此规避单个 dex 文件中的方法总数不得超过 65536 的限制。

    要知道,dalvik 强制限定了一个 APK 包中只能包含一个字节码文件。 这篇文章提供了一种有趣的思路,帮助我们理解为什么分割字节码文件是可行的。

    相关文章

      网友评论

      • song4:抛出这个异常的代码在这里:

        ```
        // dalvik/dx/src/com/android/dx/merge/InstructionTransformer.java
        private static void jumboCheck(boolean isJumbo, int newIndex) {
        if (!isJumbo && (newIndex > 0xffff)) {
        throw new DexIndexOverflowException("Cannot merge new index " + newIndex +
        " into a non-jumbo instruction!");
        }
        }
        ```

        虚拟机在运行 `jumboCheck` 的时候,会判断 `nexIndex` 是否超过了 0xffff(也就是65535)。为什么做这个检查呢?因为要确保 `IndexMap` 里面的数组不会越界。
      • alighters:这个原因是有点商榷。。
      • 迷途小书童nb:骚年,你理解错了,要是只改个short这么简单,安卓设计者也显得太弱了。
        af55d2215dec:@Adley 有可能其他地方以这个为基础做了限制?
      • 程序亦非猿:厉害 加油

      本文标题:一个诡异的 Android 大坑

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