Android为什么方法数不能超过65535
言归正传,来聊聊为什么方法数不能超过65535?搬上Dalvik工程师在SF上的回答,因为在Dalvik指令集里,调用方法的invoke-kind
指令中,method reference index只给了16bits,最多能调用65535个方法,所以在生成dex文件的过程中,当方法数超过65535就会报错。细看指令集,除了method,field和class的index也是16bits,所以也存在65535的问题。一般来说,method的数目会比field和class多,所以method数会首先遇到65535问题,你可能都没机会见到field过65535的情况。
幸运的我见到了,呵呵。
错误
明明显示的是DexException: Too many classes in --main-dex-list
,和field有毛关系?你可能想问我脑子是不是进水了,唉,明明是Google工程师脑子有问题,你看看Google自家兄弟提的issue就知道了。在使用multidex的情况下,主dex不管method还是field数目超了65535都会报Too many classes的错误,不使用multidex,就报正常的错误了,呵呵。
你可能要纳闷了,我为啥会遇到field超65535的问题?看看我”新大美”同事写的,哈哈:
实际应用中我们还遇到另外一个比较棘手的问题, 就是Field的过多的问题,Field过多是由我们目前采用的代码组织结构引入的,我们为了方便多业务线、多团队并发协作的情况下开发,我们采用的aar的方式进行开发,并同时在aar依赖链的最底层引入了一个通用业务aar,而这个通用业务aar中包含了很多资源,而ADT14以及更高的版本中对Library资源处理时,Library的R资源不再是static final的了,详情请查看google官方说明,这样在最终打包时Library中的R没法做到内联,这样带来了R field过多的情况。
差不多的情况,我们上层十几个业务线为独立module,都依赖base,而base的资源id有个三四千,上层R文件会把下层的R文件合并过来,使用multidex后,会把manifest里的activity、service等和其直接引用类加到main dex中,所以很多R文件涌入,field超个65535那都不叫事。
修改R
field这么多怎么办呢?我们大胆假设只保留最顶层的R文件,因为这个R文件会把下层R文件合并过来,所有的R引用都可以指向这个文件。下层的类要引用最上层的R文件,下层不可能依赖上层,所以修改源代码肯定是走不通的,那就改class文件字节码吧。遍历class文件,把R的引用都指向最上层,把其他没用的R文件删掉。
问题
思路有了,接下来的操作有以下问题:
什么时候修改?
dex过程是把全部class文件转换成dex文件,所以class字节码的修改要在dex之前,我们决定介入构建流程,在dex之前添加一个gradle任务,用来修改字节码。
用什么修改?
可以使用asm这个库,由于android gradle间接依赖asm,所以我们可以在build.gradle中直接import相关类。
修改什么?
当然是修改class文件,那么class文件的路径在哪里?主工程的
build/intermediates/exploded-aar
中包含了库工程aar解压后的内容,有很多jar文件,这些jar文件太过分散不好操作,由于我们使用了multidex,看一下dex任务的输入,发现是主工程的build/intermediates/multi-dex/common/debug/allclasses.jar
文件,顾名思义,这个文件包含了所有的class文件,我们直接修改这个jar包里的class文件就可以了。
代码
将下面这段代码放在主工程的build.gradle就不报错了。在我们的代码中,unifyRImport任务能跑个10秒钟左右,说长不长,说短不短。
|
坑
我一般不喜欢用坑这个词,感觉遇到坑更多是因为无知,但特马这的确是个坑,在mac上跑的好好的,windows跑不通了,还是报class太多的错误。我一想两个系统文件分隔符不同,不会是路径上出了问题吧,最后竟然发现是tmpFile.renameTo(it);
这行命令没有重命名成功,Google一搜发现遇到这坑的不在少数,a重命名成b,如果b已经存在,mac的做法直接覆盖,windows就会重命名失败。所以最后在rename前面加了一句,it.delete();
。
dex任务增量
代码欢乐的跑了几天,有哥们提意见了,什么代码就不改,为什么点击run还要跑一两分钟?在这段时间的背后AS背地里做了什么?数百头母驴为何半夜惨叫?小卖部安全套为何屡遭黑手?女生宿舍内裤为何频频失窃?连环强奸母猪案,究竟是何人所为?老尼姑的门夜夜被敲,究竟是人是鬼?数百只小母狗意外身亡的背后又隐藏着什么?这一切的背后, 是人性的扭曲还是道德的沦丧?是性的爆发还是饥渴的无奈?唉,真是崇拜爱哥。
我们可以在AS中看到dex任务又重新跑了一遍,主要时间就花在这上面了。之前的博客讲过,任务增量构建要求输入和输出较上次没有区别,dex重新跑说明输入或者输出有变化,输出是多个dex文件我们没有改动,输入allclasses.jar虽然有更改,但因为源码不变,第二次运行allclasses.jar应该和上次一样的,不应该重新跑啊。比较了两次运行的allclasses.jar的md值,发现还真是不一样啊,看来问题就出在这里了。
zip哈希
关键是为什么前后两次运行allclasses.jar的哈希值不同呢?
话说之前向maven上打包上传aar的时候,发现代码资源都不改动,上传上去的aar哈希值竟然不同,为什么呢?一个简单的a.txt前后zip压缩两次,得到的zip文件哈希值也不同,用beyond compare看了下二进制,还真的不一样。那就去看看zip算法)吧,可以看到header中有时间戳相关的东西,应该就是这导致同样的文件zip压缩后哈希值不同。
jar打包也是用的zip算法,因为第一次运行我们修改了allclasses.jar,导致第二次运行时,某个任务的输出发生了变化,所以会重新运行生成allclasses.jar,前后两次的allclasses.jar哈希值就发生了变化,dex任务就要重新跑了。
增量思路
之前的问题,主要还是没有把allclasses.jar及时还原。因为allclasses.jar是dex的输入,所以我们需要在dex之后把allclasses.jar还原,既然需要还原,那就需要在修改allclasses.jar的时候有个备份(classes.bak)。还有个问题,每次unifyRImport任务运行时,都要重新去生成精简后的allclasses.jar,这一步可以加上缓存,根据allclasses.jar的md5值命名缓存文件(.jar.opt),如果有缓存直接复制成allclasses.jar就可以了。
代码
Talk is cheap. Show me the code.
|
这次主要修改了afterEvaluate里面的东西,然后新加了自定义的md5和copyFileUsingStream方法,groovy都有些脚本的特性了,获取md5和复制文件还要自己撸,我也是醉了。
dex增量
至此,Field 65535的问题基本上算是完美解决了。但是你会发现改了一行代码,build的时间还是很久,主要耗时的任务就是dex,这个怎么搞?
两种方案,Buck和LayoutCast。Buck是facebook出品的,微信很早就用上了,但有很多规则,侵入性较强,代码改动大。 LayoutCast是我司屠大师研发的,对项目改动非常小,应该也有借鉴buck的一些思路。
稍微看了一下buck的思路,buck的dex粒度非常小,每个module都会打成一个dex,最后合并成一个大的dex,修改代码后,只需要重新生成代码所在的dex,然后通过adb传递到手机,动态替换该dex即可,都不需要重新生成apk,也节省了安装的时间。
Android为什么方法数不能超过65535的更多相关文章
- Android方法数不能超过65535
为什么方法数不能超过65535?搬上Dalvik工程师在SF上的回答,因为在Dalvik指令集里,调用方法的invoke-kind指令中,method reference index只给了16bits ...
- 彻底解决Android 应用方法数不能超过65K的问题
作为一名Android开发者,相信你对Android方法数不能超过65K的限制应该有所耳闻,随着应用程序功能不断的丰富,总有一天你会遇到一个异常: Conversion to Dalvik forma ...
- 解决Android 应用方法数不能超过65K的问题
Conversion to Dalvik format failed:Unable toexecute dex: method ID not in [0, 0xffff]: 65536 假设你的应用出 ...
- Android的方法数超过65535问题
Under the Hood: Dalvik patch for Facebook for Android 先来看一段中文内容 Hack Dalvik VM解决Android 2.3 DEX/Line ...
- Android方法数methods超过65536
当Android App中的方法数超过65535时,如果往下兼容到低版本设备时,就会报编译错误: Cannot fit requested classes in a single dex file. ...
- Android工程方法数超过64k,The number of method references in a .dex file cannot exceed 64K.
最近将一个老的Eclipse项目转到Android Studio后,用gradle添加了几个依赖,项目可以make,但是一旦run就报错 Error:The number of method refe ...
- Android 65536方法数限制的思考
前言 没想到,65536真的很小. 1 Unable to execute dex: method ID not in [0, 0xffff]: 65536 PS:本文只是纯探索一下这个65K的来源, ...
- 谈谈如何查看Android项目方法数
我们都知道,Android App的方法数是有天花板的,在方法数达到65536时,就会出现打包异常,这个时候,我们需要去除一些不需要的三方工具包,或者采用多Dex技术分包,都能达到正常打包的效果. 可 ...
- Android工程方法数超过65535的解决办法
Error:Execution failed for task ':ttt:transformClassesWithDexForDebug'.com.android.build.api.transfo ...
随机推荐
- IOS开发---菜鸟学习之路--(十五)-如何实现拍照功能
本章将来讲解下如何实现拍照的功能 我们需要的实现的效果是 好了 直接开始内容吧 首先我们需要新建一个ViewController 就叫AddPictureViewController 然后选 ...
- pb8.0 mssqlserver 新建数据库连接问题
将ntwdblib.DLL复制到Sybase\Shared\PowerBuilder目录下 unable to load the requested database interface,无法创建数据 ...
- [python][django学习篇][3]创建django web的数据库模型
推荐学习博客:http://pythonzh.cn/post/8/ 博客或者web界面向用户展示内容,它需要从某个地方获取博客内容或者web界面内容,才能够展示出来.通常来说:某个地方指的就是数据库 ...
- idea使用maven逆向mybitis的文件
引用自 http://blog.csdn.net/for_my_life/article/details/51228098 本文介绍一下用Maven工具如何生成Mybatis的代码及映射的文件. 一. ...
- jquery的html、text、val的用法
.html()用为读取和修改元素的HTML标签 .text()用来读取或修改元素的纯文本内容 .val()用来读取或修改表单元素的value值. 这三个方法功能上的对比 .html(),.text() ...
- col-md-push-*和col-md-offset的区别
实现方式的区别:col-md-offset-*,是利用margin-left实现的,col-md-push-*/col-md-pull-*是利用相对定位实现的. 效果的区别,col-md-offset ...
- HTML中使用JavaScript
1.script中使用src引入外部js.注意:不能是<script />,必须是<script></script> 2.使用link引入外部js 3.使用scri ...
- P3078 [USACO13MAR]扑克牌型Poker Hands
题目描述 Bessie and her friends are playing a unique version of poker involving a deck with N (1 <= N ...
- BZOJ 3456 城市规划 ——NTT
搞出递推式. 发现可以变成三个函数的乘积. 移项之后就可以求逆+NTT做了. miskoo博客中有讲 #include <map> #include <cmath> #incl ...
- SQLalchemy 使用记录
1.models.py中添加该方法,可通过该方法转dict #驼峰 def to_hump_dict(self): return {commonUtils.str2Hump(c.name): geta ...