Android出现65535的原因

Android应用在方法数量超过65535时,会出现编译错误。这是因为Dalvik和ART虚拟机在加载.dex文件时,DexHeader结构对方法和属性总数限制为65535。Google为此提供了multi-dex解决方案。编译时的错误提示在MemberIdsSection.java,而DexHeader在native层中方法和属性数量字段为4个字节,可能的原因是预留扩展空间。此外,属性总数超过65535也会导致相同问题。

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

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

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


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

      

         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。


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

推断:

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

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


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



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

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


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



总结: 

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

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

      

关于64 k引用限制 Android应用程序(APK)在Dalvik可执行文件的形式包含可执行的字节码文件(DEX)文件,其中包含已编译的代码来运行你的应用程序。Dalvik可执行规格限制一个Dex文件包含65536个方法:包括Android框架方法、Library方法的总数、和你自己的代码方法总数。因为65536等于64×1024,这一限制被称为“64k引用限制”。 这个极限就要求我们配置应用程序的构建过程,需要生成多个DEX文件,所以称为multidex 配置。 分析原因与注意事项 解决方法分Android 5.0及以上系统和5.0以下系统怎么做。客官们不要着急,先看我一个个分析原因,毕竟我要装下逼哈哈。 一、Android 5.0以下的版本 Android 5.0(API leve 21)之前的系统使用Dalvik执行应用程序代码。默认情况下,Dalvik限制一个apk只有一个Dex文件。为了绕过这个限制, 我们可以使用multidex support library,它成为我们APK的主要DEX文件的一部分,负责管理我们APK访问其他DEX文件和代码。 注意: 如果咱的项目minSdkVersion是20或更低,运行到Android 4.4(API leve 20)或者更低版本的设备上时需要禁用AndroidStudio的即时运行 二、Android 5.0和更高版本 Android 5.0(API leve 21)和更高的系统使用runtime是ART ,原生支持从应用的apk文件加载多个DEX文件。ART在安装应用时预编译应用程序,会扫描多个classes(..N).dex文件编译成一个.oat的文件。更多Android5.0 runtime的更多信息,请参见即时运行-instant-run。 注意: 如果你使用即时运行 , AndroidStudio自动配置你的应用程序,你应用程序的minSdkVersion应该设置为21或更高。因为即时只工作在你APP的Debug版本,你任然需要配置你的release版本构建时用multidex避免64k的限制。
### Android Studio 编写的 App 闪退原因及解决方案 #### 一、常见闪退原因分析 1. **Debug 和 Release 模式的差异** 如果在 Android Studio 的主界面左下角的 “Build Variants” 中选择了 Debug 模式,可能会导致某些优化未生效,从而引发闪退。将 Build Variant 修改为 Release 后重新构建项目通常能解决问题[^2]。 2. **API 版本不匹配** 当模拟器使用的 API 版本与 `build.gradle` 文件中指定的 `compileSdkVersion` 或 `targetSdkVersion` 不一致时,可能导致兼容性问题并引起闪退。例如,如果模拟器使用的是 API 29 而项目的 Gradle 文件却指定了 API 30,则需要调整 Sdk Version 和 Target Version 至相同版本[^3]。 3. **第三方库初始化异常** 初始化第三方 SDK(如 Bmob 数据库)时可能触发错误。例如,调用 `Bmob.initialize()` 方法后可能出现 `sources code does not match the bytecode` 错误。这种情况下应检查模拟器和编译环境的 API 是否一致,并确保依赖项正确配置。 4. **静态接口方法的支持限制** 使用静态接口方法时,若目标设备的操作系统低于 Android N (API Level 24),则会抛出 `Static interface methods are only supported starting with Android N (--min-api 24)` 错误。此时需升级最低支持版本或将代码逻辑改为动态实现方式[^3]。 5. **SELinux 配置不当** 在嵌入式开发板环境中调试 APP 时,SELinux 默认处于 enforcing 模式,这可能阻止部分操作而导致闪退。可以通过修改 `/rk3568_android_sdk/kernel/.config` 将 SELinux 设置为 permissive 模式或者通过命令行设置 `setenforce 0` 来临时禁用强制策略[^4]。 6. **Dex 方法数量超出限制** 对于较老版本的 Android 系统(如 4.4 及以下),单个 APK 文件内的方法总数不得超过 65535(即 64K)。当应用规模较大时容易突破此限制,进而发生无法加载类的现象。Google 提供了 MultiDex 技术作为应对方案来分割 dex 文件[^5]。 7. **资源文件冲突或缺失** 若布局 XML 文件存在语法错误、引用不存在的图片或其他资源也可能造成崩溃现象。务必仔细核查 res 目录下的所有资源定义是否准确无误。 --- #### 二、具体排查流程 以下是针对以上提到的各种可能性所设计的一套通用诊断步骤: 1. 查看 Logcat 日志输出,定位具体的 Exception 类型及其堆栈信息; 2. 根据日志提示确认是否存在外部服务集成失败情况,比如网络请求框架 Retrofit/Firebase 初始话过程是否有异常; 3. 审核 manifest 清单声明权限是否齐全以及 activity 注册是否遗漏; 4. 测试不同物理机型/虚拟机组合验证是否属于特定硬件架构适配难题; 5. 更新 gradle 插件到最新稳定版以获得更好的性能表现和支持特性改进; 6. 实施 multidex 支持机制扩展可容纳更多 method count 上限范围; --- #### 三、代码实例展示 下面给出一段关于如何开启 Multidex 功能的例子: ```gradle // build.gradle 文件片段 android { ... defaultConfig { multiDexEnabled true // 开启MultiDex选项 } } dependencies { implementation 'com.android.support:multidex:1.0.3' } // Application 子类重载 attachBaseContext 函数 @Override protected void attachBaseContext(Context base) { super.attachBaseContext(base); MultiDex.install(this); // 加载额外dex档案 } ``` --- ###
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值