前段时间做项目,因为项目应用了太多的第三方库和包,也出现了著名了“64K”问题。就是说代码中的一些东西超出了Android本身架构的极限。当时太着急,找到了解决的办法就搁置了。今天在看Android官方文档时发现,Android对这个问题早就做了处理。所以今天在此粘贴复制,做一个记录。

随着 Android 平台的持续成长,Android 应用的大小也在增加。当我们的应用及其引用的库达到特定大小时,就会遇到构建错误,指明应用已达到 Android 应用构建架构的极限。早期版本的构建系统按如下方式报告这一错误:

Conversion to Dalvik format failed:
Unable to execute dex: method ID not in [0, 0xffff]: 65536

较新版本的 Android 构建系统虽然显示的错误不同,但指示的是同一问题:

trouble writing output:
Too many field references: 131000; max is 65536.
You may try using --multi-dex option.

这些错误状况都会显示下面这个数字:65,536。这个数字很重要,因为它代表的是单个 Dalvik Executable (dex) 字节码文件内的代码可调用的引用总数。如果构建的 Android 应用收到了这个错误,那么恭喜你!本文介绍如何越过这一限制,继续构建我们的应用。

关于 64K 引用限制:

Android 应用 (APK) 文件包含 Dalvik Executable (DEX) 文件形式的可执行字节码文件,其中包含用来运行我们的应用的已编译代码。Dalvik Executable 规范将可在单个 DEX 文件内可引用的方法总数限制在 65,536,其中包括 Android 框架方法、库方法以及自己代码中的方法。在计算机科学领域内,术语千(简称 K)表示 1024(或 2^10)。由于 65,536 等于 64 X 1024,因此这一限制也称为“64K 引用限制”。
越过这一限制需要您将应用构建流程配置为生成多个 DEX 文件,这种配置称为 Dalvik 可执行文件分包配置。

Android 5.0 之前版本的 Dalvik 可执行文件分包支持

Android 5.0(API 级别 21)之前的平台版本使用 Dalvik 运行时来执行应用代码。默认情况下,Dalvik 限制应用的每个 APK 只能使用单个 classes.dex 字节码文件。要想绕过这一限制,我们可以使用 Dalvik 可执行文件分包支持库,它会成为您的应用主要 DEX 文件的一部分,然后管理对其他 DEX 文件及其所包含代码的访问。

Android 5.0 及更高版本的 Dalvik 可执行文件分包支持
Android 5.0(API 级别 21)及更高版本使用名为 ART 的运行时,后者原生支持从应用 APK 文件加载多个 dex 文件。ART 在应用安装时执行预编译,扫描 classes(..N).dex 文件,并将它们编译成单个 .oat 文件,供 Android 设备执行。

规避 64K 限制

在将我们的应用配置为支持使用 64K 或更多方法引用之前,我们应该采取措施减少应用代码调用的引用总数,包括由我们的应用代码或包含的库定义的方法。下列策略可帮助避免达到 dex 引用限制:
检查应用的直接和传递依赖项 - 确保在应用中使用任何庞大依赖库所带来的好处大于为应用添加大量代码所带来的弊端。一种常见的反面模式是,仅仅为了使用几个实用方法就在应用中加入非常庞大的库。减少您的应用代码依赖项往往能够帮助您规避 dex 引用限制。
通过 ProGuard 移除未使用的代码 - 为应用配置 ProGuard 设置以运行 ProGuard,并确保为发布构建启用压缩。启用压缩可确保您交付的 APK 不含有未使用的代码。
运用这些技巧可帮助我们避免为了在应用中支持更多的方法引用而需要进行的构建配置变更。这些措施还可以减小 APK 的大小,这对带宽成本较高的市场特别重要。

通过 Gradle 配置应用进行 Dalvik 可执行文件分包

Android SDK Build Tools 21.1 及更高版本中提供的 Android Plugin for Gradle 支持以 Dalvik 可执行文件分包作为构建配置的一部分。请务必使用 SDK 管理器将 Android SDK Build Tools 工具和 Android 支持存储区更新至最新版本,然后再尝试配置您的应用进行 Dalvik 可执行文件分包。
将应用开发项目设置为使用 Dalvik 可执行文件分包配置需要对我们的应用开发项目做几项修改。具体地讲,您需要执行以下步骤:

1.将 Gradle 构建配置更改为启用 Dalvik 可执行文件分包
      2.修改清单文件以引用 MultiDexApplication 类

修改模块级 build.gradle 文件配置以加入支持库和启用 Dalvik 可执行文件分包输出,如下面这段代码中所示:

android {compileSdkVersion 21buildToolsVersion "21.1.0"defaultConfig {...minSdkVersion 14targetSdkVersion 21...// Enabling multidex support.multiDexEnabled true  //设置true }...
}dependencies {compile 'com.android.support:multidex:1.0.0'
}

在清单中,将 Dalvik 可执行文件分包支持库中的 MultiDexApplication 类添加到 application 元素中

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"package="com.example.android.multidex.myapplication"><application...android:name="android.support.multidex.MultiDexApplication">...</application>
</manifest>

将这些配置设置添加到应用后,Android 构建工具会根据需要构建主 dex (classes.dex) 和辅助 dex(classes2.dex、classes3.dex)。随后构建系统会将它们打包成一个 APK 文件进行分发。

注:如果我们应用用途会扩展 Application 类,我们可以替换 attachBaseContext() 方法并调用 MultiDex.install(this) 来启用 Dalvik 可执行文件分包。

Dalvik 可执行文件分包支持库的局限性:

Dalvik 可执行文件分包支持库具有一些已知的局限性,将其纳入我们的应用构建配置之中时,应该注意这些局限性并进行针对性的测试:
启动期间向设备数据分区中安装 .dex 文件的过程相当复杂,如果辅助 dex 文件较大,可能导致应用无响应 (ANR) 错误。在此情况下,我们应该通过 ProGuard 运用代码压缩技巧来尽量减小 dex 文件的大小,并移除未使用的那部分代码。
由于存在 Dalvik linearAlloc 错误,使用 Dalvik 可执行文件分包的应用可能无法在运行的平台版本低于 Android 4.0(API 级别 14)的设备上启动。如果您的目标 API 级别小于 14,请务必针对这些版本的平台进行测试,因为应用可能会在启动时或加载特定类群时出现问题。代码压缩可以减少甚至有可能消除这些潜在问题。
由于存在 Dalvik linearAlloc 限制,如果使用 Dalvik 可执行文件分包配置的应用发出非常庞大的内存分配请求,可能会在运行时发生崩溃。尽管 Android 4.0(API 级别 14)提高了分配限制,但在 Android 5.0(API 级别 21)之前的 Android 版本上,应用仍有可能遭遇这一限制。
系统对于主 dex 文件在 Dalvik 运行时中执行时需要哪些类有着复杂的要求。Android 构建工具更新会处理这些 Android 要求,但所包括的其他库也可能具有其他依赖项要求,包括使用自检机制,或从原生代码调用 Java 方法。一些库可能要等到 Dalvik 可执行文件分包构建工具在更新后允许您对必须包括在主 dex 文件中的类进行指定时才能使用。

了解了原因,并完成以上过程操作后编译项目,就不会出现64k的问题。若有相关更难解决的问题或者需要更详细的可以看官方文档:

https://developer.android.com/studio/build/multidex.html#mdex-gradle

Android 中的“64k”问题和分包相关推荐

  1. Android中应用分包的方法(Apk Splits)

    通常情况下,应用会根据不同的设备尺寸,准备不同的资源文件,以不同的资源修饰符进行区分. 例如,不同尺寸的图片将分别放入到drawable-mdpi.drawable-hdpi等文件夹. 然而,对于一个 ...

  2. Android中65536问题剖析

    问题出现的原因是因为导入融云通信的包后,突然提示: Error:The number of method references in a .dex file cannot exceed 64K.  L ...

  3. Gradle在Android中的简单使用

    Gradle在Android中简单的使用 还望支持个人博客站:http://www.enjoytoday.cn Android Studio 使用gradle进行工程构建,为了更好的了解整个andro ...

  4. 【Framework】透视Android中的Handler

    准备对基于Android应用开发Framework层的内容进行学习回顾,学习一个新技术前我们一般都会灵魂三问:What-Why-How(是什么.为什么.怎么用).源码的学习一定要亲自去看,用IDE或者 ...

  5. Android中网络编程

    在Android中几种网络编程的方式: (1)针对TCP/IP的Socket.ServerSocket (2)针对UDP的DatagramSocket.DatagramPackage.这里需要注意的是 ...

  6. Android中实现为TextView添加多个可点击的文本

    这篇文章主要介绍了Android中实现为TextView添加多个可点击的文本,可实现类似Android社交软件显示点赞用户并通过用户名称进入该用户主页的功能,是非常实用的技巧,需要的朋友可以参考下.具 ...

  7. android 弹出fragment,Android中ViewPager获取当前显示的Fragment

    前言 在项目中,有时会用到在ViewPager中显示同样类型的Fragment,同时这样的Fragment的个数是动态的,但是PagerAdapter没有给我们提供getCurrentFragment ...

  8. android 读取内部存储文件格式,Android中的数据储存之文件存储

    当我们在使用各种程序时,其实际上是在和各种数据打交道,当我们聊QQ,刷微博,看新闻,其实都是在和里面的数据交互 例如在聊天时发出的消息,以及在登录时输入的账号密码,其实都是瞬时数据,那什么是瞬时数据呢 ...

  9. android中一种不支持的lua操作

    今天写了一段lua代码,在win32中正常运行,在android中运行无效. 大概是这样的: ------file1.lua----- local t = {} t.str = "this ...

最新文章

  1. Revit二次开发之“选择某一楼层的墙”
  2. linux ifconfig找不到
  3. android 追加写入数据到文件
  4. java form 对象 一对一_java-双向一对一地“对象引用了一个未保存...
  5. 简单版:带干扰线的图形验证码生成
  6. linux下命令集合
  7. 《程序设计技术》第四章例程
  8. java中的类方法和实例方法_下面关于Java语言中实例方法和类方法的说法,哪几项是对的?...
  9. java Session缓存
  10. WIN10添加纯英文输入法
  11. qt设置进程开机自启动
  12. php 过滤微信符号昵称,PHP处理微信昵称特殊符号过滤的方法
  13. Linux日文教程,Ubuntu日语、法语输入法的设置
  14. 本田智能驾驶功能介绍-TSR/CTM/MVC360
  15. Unit 3-Lecture 5: The Pigeonhole Principle and Inclusion-Exclusion
  16. 网吧用计算机性能配件清单,网吧主机都是什么配置?看看清单就知道!
  17. bagku秋名山老司机
  18. 用Node.js实现一个HTTP服务器程序(文件服务器)
  19. 解决chrome浏览器手机调试模式下鼠标指针消失(量化范围设置无效情况)
  20. 深入理解23种设计模式(14) -- 访问者模式

热门文章

  1. JavaSE基础项目:拼图小游戏
  2. UML2.0最新版入门图解
  3. 任正非正面回应!万字问答全文来了,涉及AI、教育、基础科学等多个重磅命题...
  4. 计算机技术在铁路中的应用,计算机容错技术在铁路信号系统中的应用
  5. PMP考试的100个关键考点
  6. 教务管理系统示例——前端模板
  7. STM32 —— 温湿度( AHT20 )传感器入门
  8. bytes和bytearray
  9. The Golem Group/University of California at Los Angeles Autonomous Ground Vehicle in the DARPA Grand
  10. ORACLE取当前日期上一个季度的开始日期和结束日期