共计 5357 个字符,预计需要花费 14 分钟才能阅读完成。
随着安卓平台的一直倒退与壮大,市场上大而全的利用亘古未有,产品需要的变更累积和 UI 交互的极致谋求,除了 resources 文件的俱增,在 Android Project 中依赖的 Library 和 本人写的 Java 代码也会越来越多。这些变动,除了会导致打包出的 APK 文件越来越大之外,当我的项目中 java 代码蕴含的办法数(method count)超出一个峰值时,编译过程中就会呈现如下谬误:
较早版本的编译系统中,谬误内容如下:
Conversion to Dalvik format failed:
Unable to execute dex: method ID not in [0, 0xffff]: 65536
而在新版编译系统中,是这样:
trouble writing output:
Too many field references: 131000; max is 65536.
You may try using --multi-dex option.
只管在不同版本的编译系统中显示的谬误内容不尽相同,但内容中都提到了一个具体的数字:65536,这个数字也是本文要讲到的核心内容:Android 64K Method Counts Limit 的峰值。
Android Project 通过编译打包,其中的 Java 代码(包含 Library)转化为 DEX 格局的字节码文件,这是 Android 5.0 之前的 Dalvik 虚拟机决定的(5.0 之后改为 ART 虚拟机),并且采纳 short 类型援用 DEX 文件中的 method,这也为 method 数量的峰值大小埋下了隐患。short 类型可能示意的最大值是 65536,也就说单个 DEX 文件中最多只有 65536 个 method 可能失去援用,如果代码执行了超出局部的 method 援用,天然会报错,如 methodNotFound 等。1K 等于 1024,65536 刚好是 64K,为了便于称说和应用,就将这个限度规定统称为 64K 办法数的援用限度。
为了解决 64K 办法数限度的问题,咱们能够在我的项目中应用 multidex 配置,当我的项目中的办法数(包含:Android framework,library 和咱们本人写的代码)超过 64K 时,编译系统会主动编译出多个 DEX 文件。
Multidex Support
Android 5.0 之前,安卓零碎采纳的是 Dalvik 虚拟机,采纳的是 JIT 技术(Just-in-time compilation,即时编译,运行时编译 DEX 字节码文件,这也是以前为什么安卓手机用户总是诟病 Android 零碎比 iOS 零碎运行卡顿的起因),限度每个 APK 文件只能蕴含一个 DEX 文件(即 classes.dex
)。为了绕开这个限度,Google 给咱们提供了 multidex support library 兼容包,帮忙咱们实现应用程序加载多个 DEX 文件,并且这个兼容包作为程序的主 DEX 文件,管理者其余 DEX 文件的拜访。
留神:因为 Instant Run 机制利用的就是 multidex 原理,当我的项目中
minSdkVersion
参数设置为 20 或者更小,并且运行在 Android 4.4 (API 20) 或更低版本的设施中时,Instant Run
将生效。
Android 5.0 之后,安卓零碎改用了 ART 虚拟机(Android RunTime),采纳的是 OAT 技术(Ahead-of-time,预编译,在利用装置的时候扫描利用中的所有 DEX 文件,并编译成一个 .oat
格局的文件供安卓设施执行,所以相比 Dalvik 虚拟机下的利用,安装时间较长)。因而能够了解为,应用 ART 虚拟机下的安卓零碎主动反对 APK 文件中多个 DEX 的加载。
留神:应用
Instant Run
时,如果我的项目中的minSdkVersion
参数设为 21 或更高版本,Android Studio 编译运行时会主动使利用反对 multidex。但Instant Run
仅仅作用于 debug 版本,咱们仍然须要给 release 版本配置 multidex 来避开 64K 办法数的限度。
Config for Multidex With Gradle
Android Gradle 插件在 Android SDK Build Tools 21.1 及更高版本的编译工具上反对 multidex 作为编译配置的一部分,所以确保咱们的 Android SDK Build Tools tools 曾经更新至 21.1 或更高版本,而后再来配置利用的 multidex 局部。
第一步,批改 app/build.grale
文件,使我的项目可能应用 multidex:
android {
compileSdkVersion 21
buildToolsVersion "21.1.0"
defaultConfig {
...
// Enabling multidex support.
multiDexEnabled true
}
...
}
dependencies {compile 'com.android.support:multidex:1.0.0'}
第二步,批改 AndroidManifest.xml
文件,援用 MultiDexApplication
类:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.yifeng.mdstudysamples">
<application
...
android:name="android.support.multidex.MultiDexApplication">
...
</application>
</manifest>
有时候,你可能还须要扭转一下 javaMaxHeapSize 的大小:
android {
dexOptions {javaMaxHeapSize "4g"}
}
增加这些配置后,编译工具会构建出一个主 DEX 文件(classes.dex)和其余从属 DEX 文件(classes2.dex,classes3.dex 等,如果需要的话),编译系统会将他们打包到 Apk 文件中。
留神:个别咱们会在我的项目中自定义一个继承自
Application
的类,此时就须要重写attachBaseContext()
办法,并在该办法外面调用MultiDex.install(this)
来反对 multidex,可参考:MultiDexApplication
Optimizing Multidex Development Builds
multidex 配置下的利用,编译系统须要通过简单的 DEX 宰割运算,导致减少我的项目的编译工夫,从而影响开发人员的开发效率。咱们能够应用 productFlavors 构建开发环境和正式环境的不同 flavors 来优化 multidex 的长时间编译问题。
对于 development flavor,设置 minSdkVersion
值为 21,运行在 Android 5.0 以上版本的设施中,应用 ART-supported 格局生成 multidex 的速度要快得多。对于 release flavor,minSdkVersion
值则设为利用理论反对的版本,编译系统消耗较长的工夫来生成适配多设施的 multidex APK 文件。如:
android {
productFlavors {
// Define separate dev and prod product flavors.
dev {
// dev utilizes minSDKVersion = 21 to allow the Android gradle plugin
// to pre-dex each module and produce an APK that can be tested on
// Android Lollipop without time consuming dex merging processes.
minSdkVersion 21
}
prod {
// The actual minSdkVersion for the application.
minSdkVersion 14
}
}
...
buildTypes {
release {
runProguard true
proguardFiles getDefaultProguardFile('proguard-android.txt'),
'proguard-rules.pro'
}
}
}
dependencies {compile 'com.android.support:multidex:1.0.0'}
这样,在开发阶段,应用 devDebug 类型的变种 app,勾销混同,反对 multidex,并且运行在 5.0 及以上版本的设施中,可能放慢编译过程。无关 flavors 的信息,以前写过一篇文章:Android 利用 Gradle 实现 app 的环境拆散,更多信息能够参考英文手册:Gradle Plugin User Guide,对应中文版译文:Gradle Android 插件用户指南翻译。
strings count limit
后面咱们说完繁多 dex 文件的办法数限度,事实上,还有一个字符串数量限度。如果我的项目没有应用 multidex 反对的话,当 strings 超出肯定限度,编译过程也会出错:
Dex: Error converting bytecode to dex:
Cause: com.android.dex.DexIndexOverflowException: Cannot merge new index 65868 into a non-jumbo instruction!
不同我的项目编译过程中报错信息里的具体数字可能不同。Dex 文件中呈现的 string 默认是 4 个字节即 16 位大小的 int 类型的数字援用应用的,即单个 Dex 文件最多只能援用 2^16 个 strings,当你的我的项目中呈现超过这个最大数字的字符串援用,而又没有应用 multidex 反对,编译过程便会出错。
对于这种状况,除了应用 multidex,还有另外一种解决方案:jumboMode。这个模式容许单个 Dex 文件反对到 32 为大小的 strings 援用,即 2^32 的援用峰值。应用办法是,在工程 app 模块下 build.gradle 文件的 android 配置下增加:
dexOptions {jumboMode true}
留神:尽管单个 Dex 文件中 strings 数量限度与 method 数量限度十分类似,然而如果我的项目办法数超过 64K,咱们还是须要应用 multidex 来解决,留神辨别。无关这方面的更多具体介绍,请参考 Dalvik bytecode。
Methods Count Statistics
只管安卓零碎反对 multidex,咱们还是要学会剖析咱们的利用,查看各个局部的办法数,缩小冗余办法。这里举荐几个工具,帮忙咱们剖析。
Library Methods count
一个在线统计 Android Library 办法数的网站,可能统计出 Android 畛域常见 libraries 的办法数、JAR 文件和 DEX 文件大小,并且可能抉择不同版本,以图表的模式展现进去。
http://www.methodscount.com/
该网站也提供了 Android Studio 的插件,帮忙咱们剖析我的项目中所依赖的 libraries 的办法数,如图所示:
methodscount-samples-02.png
methodscount-samples-03.png
Apk Method Count
一个在线统计 APK 文件办法数的开源我的项目,只须要将须要剖析的 APK 文件拖拽上传至此,即可失去剖析后果,如图:
apk-method-count-samples.png
Android Studio APK Analyzer
最初,要重磅举荐 Android Studio 自带的 APK Analyzer,功能齐全,使用方便,相对是安卓开发人员剖析利用的不二抉择。应用 Android Studio APK Analyzer,咱们至多可能做到:
- 查看 APK 压缩文件中各个子文件的大小(如 DEX 和 resource 文件)
- 了解 DEX 文件的构造
- 疾速查看 APK 文件的版本信息(间接查看
AndroidManifest.xml
内容) - 直观地比拟两个 APK 文件内容
Android-Studio-APK-Analyzer-Samples.png
开发阶段应用 Android Studio 关上一个我的项目时,有三种形式应用 APK Analyzer 工具:
- 间接拖拽 APK 文件到 Android Studio 的编辑窗口
- 双击关上我的项目目录
app/build/outputs/apk/
下的 APK 文件 -
点击菜单栏
Build->Analyse APK...
并抉择 APK 文件相干教程
Android 根底系列教程:
Android 根底课程 U - 小结_哔哩哔哩_bilibili
Android 根底课程 UI- 布局_哔哩哔哩_bilibili
Android 根底课程 UI- 控件_哔哩哔哩_bilibili
Android 根底课程 UI- 动画_哔哩哔哩_bilibili
Android 根底课程 -activity 的应用_哔哩哔哩_bilibili
Android 根底课程 -Fragment 应用办法_哔哩哔哩_bilibili
Android 根底课程 - 热修复 / 热更新技术原理_哔哩哔哩_bilibili