Android出现65535的原因

       众所周知当Android app代码足够多时, 编译会报方法数超过65535问题。 Google为了解决这个问题,提供了multi-dex方案, 即将代码编译成若干个dex文件,如classes.dex, classes2.dex...classes*.dex。 

      我们关心的是怎么解决这个问题,包括插件化、multi-dex等等方案,  网上相关的博客非常多。

      但是有人想过Android为什么要报这个问题, 在哪里报的?


      dalvik或art虚拟机在加载*.dex文件时会生成一个DexFile实例, 该实例包含dex文件的各种属性。

     Android出现65535的原因_第1张图片 

         DexFile结构体的第一个属性pOptHeader指针的数据类型是DexHeader, 看看DexHeader的定义:

/*
 * Direct-mapped "header_item" struct.
 */
struct DexHeader {
    u1  magic[8];           /* includes version number */
    u4  checksum;           /* adler32 checksum */
    u1  signature[kSHA1DigestLen]; /* SHA-1 hash */
    u4  fileSize;           /* length of entire file */
    u4  headerSize;         /* offset to start of next section */
    u4  endianTag;
    u4  linkSize;
    u4  linkOff;
    u4  mapOff;
    u4  stringIdsSize;
    u4  stringIdsOff;
    u4  typeIdsSize;
    u4  typeIdsOff;
    u4  protoIdsSize;
    u4  protoIdsOff;
    u4  fieldIdsSize;
    u4  fieldIdsOff;
    u4  methodIdsSize;
    u4  methodIdsOff;
    u4  classDefsSize;
    u4  classDefsOff;
    u4  dataSize;
    u4  dataOff;
};

           从DexHeader的定义可以看出, Android对方法总数和属性总数都限制为4个字节即65535。大概是下午犯困惯性思维写错了, 无符号整型4个字节取值范围为0~2^32-1。

Android出现65535的原因_第2张图片

         方法、属性、类信息等等都有对象的offset, 为什么呢?

推断:

        一个dalvik或者art虚拟机可以加载若干个dex文件, 在framework层限制了单个dex的最大方法数量、最大属性数量等等(详见DexFormat.java)。 感觉methodIdsSize数据类型应该是u2, 不明白为什么是u4。。。

       offset是位置偏移,用于获取对应属性的头指针。 而每个属性对应连续的内存空间且单个实例(例如一个方法)占用空间大小相等, 虚拟机做个循环加载各个属性。 详见DexFile.cpp的dexFileSetupBasicPointer函数, 即每个属性分配头指针。Android出现65535的原因_第3张图片


        PS:我们写代码时一般是方法数量比属性数量多, 所有一般提示方法总数超过65535; 其实属性总量超过65535个时,也会报同样的问题!

Android出现65535的原因_第4张图片


         从Native层DexFile结构体定义可以看出, 每个dex文件的方法数和属性数必须都小于65535,  否则会越界(因为只有4个字节)。

        那么编译时的错误是哪里提示的呢? 经过搜索代码, 发现是在MemberIdsSection.java提示的。

Android出现65535的原因_第5张图片

         DexFormat.MAX_MEMBER_IDX等于65535, 所以编译时会报错。 

Android出现65535的原因_第6张图片


总结: 

1、framework层限制了单个dex文件的最大方法数量、最大属性数量等等为65535(占2个字节),但native层DexHeader中对应字段定义为4个字节, 暂时没找出原因。。。 

2、DexHeader各属性对应的offset是偏移, 用于获取真正的头指针。  例如dex文件含有10000个方法, methodIdsSize等于10000,通过methodIdsOff拿到头指针, 就可以读取这10000个方法各自的属性。

      

你可能感兴趣的:(Android)