久仰的方法数超65k,终于遇到了

来源:互联网 发布:h6837wi网络摄像机 编辑:程序博客网 时间:2024/04/29 04:12
解决方案:http://blog.csdn.net/t12x3456/article/details/40837287
http://www.cnblogs.com/wingyip/p/4496028.html
 
前几天做电影音乐加入虾米的SDK就发现run不起来了,提示大概就是方法数超过了65000的问题。
eclipse提示是这样的:
Unable to execute dex: method ID not in [0, 0xffff]: 65536
Conversion to Dalvik format failed: Unable to execute dex: method ID not in [0, 0xffff]: 65536
Dex 64k method size limit
.dex 是 Dalvik EXecutable,里面存的是 dex byte code,可在 Davlik VM 上执行。你 unzip 解开 .apk 后就会看到一个 classes.dex ,就是它了。不过,dex method 64k 上限数跟 dex 的格式无关。根据 stackoverlfow 回应的说法,是因为 Dalvik 指令集里,执行 method 的invoke-kind index 大小只给了 16bit,所以一个 Android 程式裡最多只能执行前 65536 个 method,后面多的都不能用。
因为是指令集的限制,所以新一代 ART Runtime 也受同样的限制。.dex 档头裡已经有写总 method 数,你可以用 Android SDK 内附的 dexdump 指令查看你的 app 定义了多少个 method:
 
cd android-sdk-macosx
./build-tools/19.1.0/dexdump -f /path/to/your/apk | grep method_ids_size
下面是输出,这个实例 apk 定义了 51306 个 method
method_ids_size     : 51306
LinearAlloc 5MB capacity
有关 LinearAlloc 的问题,網路上已經有很好的解說。简单说就是 Android 程式执行前会将 class 读进 LinearAlloc 这块 buffer 裡,它的大小在 Android 2.3 之前是 5MB,到了 4.0 后才改成 8MB 或 16MB。5MB 太小了,通常你还没踩到 64k method 限制时,就会先踩到 LinearAlloc 的问题。这个问题到了 4.0 才改善,但是 2.3 还有约十几 % 的市场,所以我们还是得面对它。注意 5MB buffer 的限制不是 classes.dex 的档案大小的限制。最主要还是看 class 结构的复杂性,以及总 method 数。
  • 根据我们的经验,总 method 数要维持在 56000 以下才能塞进 5MB buffer 裡。
  • 根据 這個 issue 22586,太复杂的 interface 继承会出问题,像是用 scala 语言开发 Android 就容易出错。
  • 某些 Andoird 2.3 的 LinearAlloc 的可用大小比其他 2.3 的还小,我们的用户中使用 HTC Desire 的手机特别容易遇到这个错误
错误信息 INSTALL_FAILED_DEXOPT
安装 apk 时,如果出现上面提到的两种错误,你通常会看到错误讯息有 INSTALL_FAILED_DEXOPT这行。dexopt 是 dex optimization 的意思,这一步骤会发生在安装完 apk 之后,它会检验 .dex 裡面的指令集是不是合法,也会验 method 的上限数。超过上限的话,app 还没啟动就被这一步挡下,直接喷错。dexopt 也会试着将所有 class/method 都读进 VM 验证,这自然会运用到 LinearAlloc buffer。如果 buffer 不够也是直接喷了。所以程式太大的话,通通会死在 dexopt 这过程里。
 
目标 method 数
好了,我们现在知道问题的成因,目标就很明确了:
开发进行期间,维持 method 数在 65536 以下 (未 proguard)
开发时通常我们会用 Android 4.0 以上的手机来测,所以不用管 56000 method 数的限制。但要确保尚未做 proguard 之前,总 method 数要小于 65536。相信我,如果开发时每次 build 都要做 proguard 才能将 method 数压在 65536 下,你会想死,每 build 一次都要几分钟以上啊。
正式发佈时,目标 method 数 56000 以下 (proguard 后)
正式发布 apk 时,proguard 这步骤通常会做。所以确保包给 Android 2.3 的版本经过 proguard 过后,method 数可以压在 56000 以下即可。
注意 56000 这数字只是我们的经验值,实际的情形可能有出入。
 
大量的 method 数哪来的
你的 App 也许才几千个 method,不过你跑一下上面的 dexdump 指令,你可能会发现你已经用掉二、三万的 method 了。有关这个问题,今年六月的时候有高手 @rotxed 詳細解說,请大家务必去读一遍。本文只是按照那篇的建议,做一些延伸性的探讨。
跑一下mihaip 開發的 dex-method-count 它是个JAVA工程,我把它直接Export JAR:
dexcount.jar
命令是:
>java -jar decount.jar you/path/apk.apk >> decount.txt
Read in 30788 method IDs.<root>: 30788 android: 8923 support: 6825 v4: 4209 v7: 2616 java: 723 javax: 5 org: 75 apache: 24 http: 24 json: 39 xmlpull: 12 v1: 12 dalvik: 2 system: 2 com: 21057 google: 21029 ads: 124 android: 20905 gms: 20905
这是一个空白的APK,但是方法书已经用掉了3w+了,所以罪魁祸首在那些第三放库上,下面给下流行的第三方库的方法书情况,以供参考:
librarymethod countfunctionjoda-time4602date timecom.fasterxml.jackson8346JSONgoogle-gson881JSONcom.squareup.okhttp1301http/spdycom.squareup.picasso445image/networkvolley376image/networkguava13587mighty toolscommons-io1196I/O toolscommons lang32415general toolsdagger268Dependency Injectionprotobuf5310protobufprotobuf-lite800protobufsquare wire + okio384 + 381protobufcom.amazonaws.services.s311798AmazonAWS SDK for SD (2.0)dropbox + misc libs412 + 2864Dropbox SDKbouncycastle8875Crypto (required by Dropbox)
很多流行的第三方库,方法数都是不菲的,所以在使用的时候一定要当心,这里给个建议,很多第三方库的功能都大大超过了我们使用范围,所以如果可能的话,尽量选那些轻量小巧的库。
可以看到square公司出的库还是很良心的,大多是专门针对Android平台开发的,大家可以优先使用它们。
总结:Method limit 是一个既爱有恨的问题,一方面代表着你的app的流行和功能庞大,另外一方面我们又得面对他,解决它,产品迭代的过程中难免会出现,网上很多拆dex的方法,这个也是最有效的解决之道,但是却治标不治本,我们使用的就是这个方法,未来对APP瘦身才是根本解决之道。
 
原创粉丝点击