当安卓工程庞大到一定程度(代码结构渣到一定程度)的时候,就会遇到诸如最大方法数超过限制导致无法安装,Crash等问题。
相关关键词:Android 2.3 INSTALL_FAILED_DEXOPT 65535
问题的本质有两个
- dx打包时限制了单个dx文件的最大方法数为65535
- Dalvik VM限制内存中加载的方法数(方法,类定义及构造函数)不能超过65535个
问题的重现很简单
- 写一个类,把函数复制个6w份,一build,报错
- apk安装到2.3系统,提示INSTALL_FAIL_DEXOPT
- 动态加载两个DEX模块,每个函数3w份,一加载运行,程序Crash
网上一般推荐的解决方法
- 删代码以及jar包,尤其是自动生成的get/set,没用的类,可以使用proguard自动优化掉无用代码
- 由于高于Gingerbread的版本将LinearAllocHdr分配空间从5M提高到8M,放弃2.3的用户后可以有一定的缓冲时间
- 使用dex动态加载的方式将程序内的模块插件化,这样会将问题1转化为问题2,如果程序加载项过大时还是会有崩溃现象出现
- 将java层逻辑移到jni层实现
- hacking dalvik vm
Facebook曾经遇到了这样的问题,有一个相关博文(Under the Hood: Dalvik patch for Facebook for Android),一通碎碎念后大概解决方法是发了一个lite版本去掉了一大票功能,以及写了一个小补丁hack掉Android Dalvik VM把它搞大了。。。
hacking dalvik vm的方法似乎是最干净利落的。可惜facebook语焉不详,参照博文中给出的信息,可以找到LinearAllocHdr*指针位于vm/Globals.h内
使用jni写了个小程序做了以下几件事情实现了该hacking
- 通过jni方法取到*env
- 指针往回便利内存查找65535对应内存块
- 重新mmap8M内存,替换到len以及Hdr的当前位置到新map的位置
代码回头再放到github。https://github.com/viilaismonster/LinearAllocFix
另外提供一个小工具可以用来反编译apk以统计构建出来的apk内*大约*有多少个方法
小工具里面会依据文件名缓存先前反编译的结果,可以用-diff参数将两个版本apk对比,查看具体到包的方法数变动
转帖:http://viila.info/2014/04/android-2-3-dex-max-function-problem/