关于dex 64K 引用限制
1.官方文档
https://developer.android.com/studio/build/multidex
主要内容:
- 什么是64K限制
- 编码时如何避免64K 限制
- 拆分dex避免64K 限制
2.DEX
DEX = Dalvik Executable , android Dalvik java 虚拟机的可执行字节码文件。APK文件包含 DEX,其中包含应用的已编译代码。
3.什么是64K限制
Dalvik Executable 规定单个 DEX 文件内可引用的方法总数限制在 65,536,其中包括 Android 框架方法、库方法以及你自己的方法。
在计算机科学领域内,术语千(简称 K)表示 1024(或 2^10)。65,536 等于 64 X 1024,因此这一限制也称为“64K 引用限制”。
当android应用中方法数量超过这个65536,打包时报错信息如下:
The number of method references in a .dex file cannot exceed 64K.
如下图:
4.查看dex引用方法的数量
用android studio 分析一个apk,看下它的classes.dex 文件。
- 这个apk内方法引用数已经达到限制,再添加一个方法、引用一个其它方法就会打包失败。
- 其中红框65536是当前引用的方法数,同时也分析出了这个应用定义了个类,有个方法。
5.编码时避免64K 限制
编码时应尽量减少应用代码中的方法数量。假如真的方法数量很多,常用避免64K限制策略如下:
5.1 减少依赖库
减少代码依赖的库数量,能不用的就不用。确保在应用中引入庞大依赖库所带来的好处大于添加大量代码所带来的弊端。
5.2 启用代码压缩
通过 ProGuard 移除未使用的代码
android {
buildTypes {
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'),
'proguard-rules.pro'
}
}
...
}
5.3 把函数放到本地so库中
在本地so库的的代码并不影响dex方法数量,假设f1()调用f2(),f3(),f4(),可以只把f1声明名native的,其它的放到so里。
下面是一个so库的源码文件,main.cpp ,里面有13w个函数,从fun_0() 到 fun_131071() ,并不影响dex.
#include <jni.h>
#include <string> extern "C" JNIEXPORT void JNICALL
Java_com_example_dex64k_MainActivity_javaFun1(JNIEnv *env,jobject /* this */) {
std::string hello = "Hello from C++";
} void
Java_com_example_dex64k_MainActivity_javaFun2(JNIEnv *env,jobject){ }
void fun_0(){ printf("hello world %s,",__func__);}
void fun_1(){ printf("hello world %s,",__func__);}
void fun_2(){ printf("hello world %s,",__func__);}
void fun_3(){ printf("hello world %s,",__func__);}
void fun_4(){ printf("hello world %s,",__func__);}
void fun_5(){ printf("hello world %s,",__func__);}
void fun_6(){ printf("hello world %s,",__func__);}
void fun_7(){ printf("hello world %s,",__func__);}
void fun_8(){ printf("hello world %s,",__func__);}
void fun_9(){ printf("hello world %s,",__func__);}
void fun_10(){ printf("hello world %s,",__func__);} ...... void fun_131062(){ printf("hello world %s,",__func__);}
void fun_131063(){ printf("hello world %s,",__func__);}
void fun_131064(){ printf("hello world %s,",__func__);}
void fun_131065(){ printf("hello world %s,",__func__);}
void fun_131066(){ printf("hello world %s,",__func__);}
void fun_131067(){ printf("hello world %s,",__func__);}
void fun_131068(){ printf("hello world %s,",__func__);}
void fun_131069(){ printf("hello world %s,",__func__);}
void fun_131070(){ printf("hello world %s,",__func__);}
void fun_131071(){ printf("hello world %s,",__func__);}
6.拆分dex避免64K 限制
把apk的dex拆分成多个,可以避免64K 限制。
6.1 api >= 21 时如何拆分dex
在模块的 build.gradle
文件中将 multiDexEnabled
设置为 true
,如下:
apply plugin: 'com.android.application' android {
compileSdkVersion 29
buildToolsVersion "29.0.1"
defaultConfig {
applicationId "com.example.dex64k"
minSdkVersion 21
targetSdkVersion 29
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
//...
}
buildTypes {
release {
minifyEnabled false
multiDexEnabled true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
//...
} dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.android.support:multidex:1.0.3'
//...
}
6.2 api < 21 时如何拆分dex
- 打开 multiDexEnabled
- 添加 com.android.support:multidex:1.0.3 依赖
android {
defaultConfig {
...
minSdkVersion 15
targetSdkVersion 28
multiDexEnabled true
}
...
} dependencies {
implementation 'com.android.support:multidex:1.0.3'
}
apk内dex文件如下:
6.3 拆分完dex后要设置application
A.未自定义application类时
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.dex64k"> <application
android:name="androidx.multidex.MultiDexApplication"
>
...
</application> </manifest>
如果用的不是androidx,那么
android:name="android.support.multidex.MultiDexApplication"
B.自定义了Application时
那么它的基类应该是 MultiDexApplication
import androidx.multidex.MultiDexApplication; public class Dex64App extends MultiDexApplication { }
如果无法修改Application的基类,那么
import android.app.Application;
import android.content.Context;
import androidx.multidex.MultiDex; public class Dex64App extends Application {
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
MultiDex.install(this);
}
}
7.拆分dex的缺点
Dalvik 可执行文件分包支持库具有一些已知的缺点.
- 启动期间在设备数据分区中安装 DEX 文件的过程相当复杂,如果DEX 文件较大,可能会导致ANR错误.
- 在api < 14 的设备上可能无法启动,也可能产生各种错误。 (http://b.android.com/22586)
- 应用发出非常庞大的内存分配请求,则可能会在运行期间发生崩溃。(http://b.android.com/78035)
8.指定某些类到主dex中
如果启动期间需要的类未在主 DEX 文件中找到,应用将崩溃并出现错误 java.lang.NoClassDefFoundError。
使用 multiDexKeepFile
或 multiDexKeepFile 可以手动将某些类指定在主 DEX 文件中。
8.1 multiDexKeepProguard
multiDexKeepProguard
文件使用与 Proguard 相同,并且支持全部 Proguard 语法。
apply plugin: 'com.android.application' android {
...
buildTypes {
release {
minifyEnabled true
multiDexEnabled true
multiDexKeepProguard file('multidex-config.pro')
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
...
}
multidex-config.pro与Module下的build.gradle同级,它内容如下:
-keep class com.example.dex64k.Dex64App
-keep class com.example.dex64k.MainActivity
-keep class com.example.dex64k.dex1
-keep class com.example.dex64k.dex2 #-keep class com.example.** { *; } // All classes in the com.example package
8.2 注意事项
- 要打开混淆选项
- 官网示例中的 multiDexKeepProguard('multidex-config.pro') 要改成 multiDexKeepProguard file('multidex-config.pro')
8.3 multiDexKeepFile
1 android {
2 compileSdkVersion 29
3 buildToolsVersion "29.0.1"
4 ...
5 buildTypes {
6 release {
7 minifyEnabled true
8 multiDexEnabled true
9 multiDexKeepFile file('multidex-config.txt')
10 proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
11 }
12 }
13 compileOptions {
14 //...
15 }
其中 multidex-config.txt 文件与Module下的build.gradle同级,它内容如下:
1 com/example/dex64k/Dex64App.class
2 com/example/dex64k/MainActivity.class
3 com/example/dex64k/dex1.class
关于dex 64K 引用限制的更多相关文章
- [转]預防 Android Dex 64k Method Size Limit
转载自:http://ingramchen.io/blog/2014/09/prevention-of-android-dex-64k-method-size-limit.html 08 Septem ...
- Conversion to Dalvik format failed:Unable toexecute dex: method ID not in [0, 0xffff]: 65536
关于方法数超限,Google官方给出的方案是这样的:https://developer.android.com/intl/zh-cn/tools/building/multidex.html 我也写过 ...
- 配置方法数超过 64K 的应用
随着 Android 平台的持续成长,Android 应用的大小也在增加.当您的应用及其引用的库达到特定大小时,您会遇到构建错误,指明您的应用已达到 Android 应用构建架构的极限.早期版本的构建 ...
- dex文件格式三
先来看看整体的结构,结构体定义在DexFile.h里面 在dexFileSetupBasicPointers中设置各个子结构体,当然是在解析DexHeader之后 源码在DexFile.c文件中 ...
- 【转】Android studio 解决64K超出链接数限制问题
http://my.oschina.net/gabriel1215/blog/602608 目录[-] 使用MultiDex支持库 注意事项 结论 如果你是一个android开发者,你至少听说过的Da ...
- Android学习笔记----解决“com.android.dex.DexIndexOverflowException: method ID not in [0, 0xffff]: 65536”问题
同时在工程中引入了多个第三方jar包,导致调用的方法数超过了android设定的65536个(DEX 64K problem),进而导致dex无法生成,也就无法生成APK文件. 解决办法如下: 1.谷 ...
- 解决“com.android.dex.DexIndexOverflowException: method ID not in [0, 0xffff]: 65536”问题(l转)
同时在工程中引入了多个第三方jar包,导致调用的方法数超过了android设定的65536个(DEX 64K problem),进而导致dex无法生成,也就无法生成APK文件. 解决办法如下: 1.谷 ...
- Android安全–Dex文件格式详解
Dex文件是手机上类似Windows上的EXE文件,dex文件是可以直接在Dalvik虚拟机中加载运行的文件. 首先我们来生成一个Dex文件. 新建文件Hello.java内容如下: class He ...
- 解决android studio上“com.android.dex.DexIndexOverflowException: method ID not in [0, 0xffff]: 65935”问题
我是在更换应用的一个jar包时发生的这个错误,网上查到说是因为同时在工程中引入了多个第三方jar包,导致调用的方法数超过了android设定的65935个(DEX 64K problem),进而导致d ...
随机推荐
- SQL语句的四种连接
SQL的四种连接查询 内连接 inner join 或者 join 外连接 左连接 left join 或者 left outer join 右连接 right join 或者 right ou ...
- 18-1-函数中this的指向
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- C++函数或者命名空间前面加::
命名空间和函数前面加上:: 经常看到命名空间前就只有:: 比如 ::test;这种代表是全局的test 比如 ::CreateDirectory(..),代表使用系统API也就是全局的 避免使用到 ...
- 莫烦PyTorch学习笔记(五)——模型的存取
import torch from torch.autograd import Variable import matplotlib.pyplot as plt torch.manual_seed() ...
- React项目开发经验汇总
博客来源 小寒的博客 定义好全局配置信息 环境变量不要提取出来,配置信息提取出来 UI样式变量 定义好变量的作用不用多说 样式库建设 工具样式,复用性强的样式,这些class成为会是真个网站样式的 ...
- ROC曲线及AUC
ROC曲线 意义 ROC曲线指受试者工作特征曲线 / 接收器操作特性曲线(receiver operating characteristic curve),是反映敏感性和特异性连续变量的综合指标,是用 ...
- SPSS科普 | 统计描述
SPSS科普 | 统计描述 统计描述的目的就是了解数据的基本特征和分布规律,为进一步合理地选择统计方法提供依据.常用的有Frequencies.Descriptives 和Explore过程. 一.F ...
- HtmlHelper2
一.隐式从ViewBag取数据 1.action中的代码: ViewBag.UserName = "admin"; cshtml中的代码: @Html.TextBox(" ...
- Node中js获取异步操作的结果
js中要获取异步操作的结果必须使用回调函数 回调函数也被称为高阶函数,简单来说就是,函数作为一个参数传到另一个主函数里面,当那一个主函数执行完之后,再执行传进去的作为参数的函数 function fn ...
- Nodejs之路(一)—— Nodejs入门
不知不觉,现在已经习惯学一点东西,就写博客记录一下.这次学习Nodejs主要是在B站上看的视频教程,感觉讲的很是不错,所以我想把在看视频学习过程中的一些重要知识点记录下来方便以后自己快速查阅. --- ...