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

解决方案: 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.: 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+了,所以罪魁祸首在那些第三放库上,下面给下流行的第三方库的方法书情况,以供参考:
library method count function
joda-time 4602 date time
com.fasterxml.jackson 8346 JSON
google-gson 881 JSON
com.squareup.okhttp 1301 http/spdy
com.squareup.picasso 445 image/network
volley 376 image/network
guava 13587 mighty tools
commons-io 1196 I/O tools
commons lang3 2415 general tools
dagger 268 Dependency Injection
protobuf 5310 protobuf
protobuf-lite 800 protobuf
square wire + okio 384 + 381 protobuf
com.amazonaws.services.s3 11798 AmazonAWS SDK for SD (2.0)
dropbox + misc libs 412 + 2864 Dropbox SDK
bouncycastle 8875 Crypto (required by Dropbox)
很多流行的第三方库,方法数都是不菲的,所以在使用的时候一定要当心,这里给个建议,很多第三方库的功能都大大超过了我们使用范围,所以如果可能的话,尽量选那些轻量小巧的库。
可以看到square公司出的库还是很良心的,大多是专门针对Android平台开发的,大家可以优先使用它们。
总结:Method limit 是一个既爱有恨的问题,一方面代表着你的app的流行和功能庞大,另外一方面我们又得面对他,解决它,产品迭代的过程中难免会出现,网上很多拆dex的方法,这个也是最有效的解决之道,但是却治标不治本,我们使用的就是这个方法,未来对APP瘦身才是根本解决之道。
 

你可能感兴趣的:(手笔)