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 引用限制的更多相关文章

  1. [转]預防 Android Dex 64k Method Size Limit

    转载自:http://ingramchen.io/blog/2014/09/prevention-of-android-dex-64k-method-size-limit.html 08 Septem ...

  2. 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 我也写过 ...

  3. 配置方法数超过 64K 的应用

    随着 Android 平台的持续成长,Android 应用的大小也在增加.当您的应用及其引用的库达到特定大小时,您会遇到构建错误,指明您的应用已达到 Android 应用构建架构的极限.早期版本的构建 ...

  4. dex文件格式三

    先来看看整体的结构,结构体定义在DexFile.h里面   在dexFileSetupBasicPointers中设置各个子结构体,当然是在解析DexHeader之后 源码在DexFile.c文件中 ...

  5. 【转】Android studio 解决64K超出链接数限制问题

    http://my.oschina.net/gabriel1215/blog/602608 目录[-] 使用MultiDex支持库 注意事项 结论 如果你是一个android开发者,你至少听说过的Da ...

  6. Android学习笔记----解决“com.android.dex.DexIndexOverflowException: method ID not in [0, 0xffff]: 65536”问题

    同时在工程中引入了多个第三方jar包,导致调用的方法数超过了android设定的65536个(DEX 64K problem),进而导致dex无法生成,也就无法生成APK文件. 解决办法如下: 1.谷 ...

  7. 解决“com.android.dex.DexIndexOverflowException: method ID not in [0, 0xffff]: 65536”问题(l转)

    同时在工程中引入了多个第三方jar包,导致调用的方法数超过了android设定的65536个(DEX 64K problem),进而导致dex无法生成,也就无法生成APK文件. 解决办法如下: 1.谷 ...

  8. Android安全–Dex文件格式详解

    Dex文件是手机上类似Windows上的EXE文件,dex文件是可以直接在Dalvik虚拟机中加载运行的文件. 首先我们来生成一个Dex文件. 新建文件Hello.java内容如下: class He ...

  9. 解决android studio上“com.android.dex.DexIndexOverflowException: method ID not in [0, 0xffff]: 65935”问题

    我是在更换应用的一个jar包时发生的这个错误,网上查到说是因为同时在工程中引入了多个第三方jar包,导致调用的方法数超过了android设定的65935个(DEX 64K problem),进而导致d ...

随机推荐

  1. <转>http协议 文件下载原理详解

    最近研究了一下关于文件下载的相关内容,觉得还是写些东西记下来比较好.起初只是想研究研究,但后来发现写个可重用性比较高的模块还是很有必要的,我想这也是大多数开发人员的习惯吧. 对于HTTP协议,向服务器 ...

  2. ajax长轮询 (转)

    javaWeb项目中需要一个实时提醒的功能,也就是某人做了某一操作,可以立即提醒到当前在线的用户 最开始想在用户做了操作后,储存一个状态到数据库中然后用每隔几秒用ajax去请求后台查询数据库来确定是否 ...

  3. LeetCode 28.实现strStr()(Python3)

    题目: 实现 strStr() 函数. 给定一个 haystack 字符串和一个 needle 字符串,在 haystack 字符串中找出 needle 字符串出现的第一个位置 (从0开始).如果不存 ...

  4. 输出内容 document.write() 可用于直接向 HTML 输出流写内容。简单的说就是直接在网页中输出内容

    输出内容(document.write) document.write() 可用于直接向 HTML 输出流写内容.简单的说就是直接在网页中输出内容. 第一种:输出内容用""括起,直 ...

  5. html--设置复选按钮和单选按钮

  6. WPF 免费控件库(2)

    最近在逛园子的时候发现的园友分享或提及的WPF控件库~ (1) Bootstrap WPF Style,Bootstrap风格的WPF样式 转:http://www.cnblogs.com/tsliw ...

  7. 学习笔记css3

    边框 盒子圆角 border-radius:5px / 20%: border-radius:5px 4px 3px 2px; 左上,右上,右下,左下 盒子阴影 box-shadow:box-shad ...

  8. 一个windows 两个jar

    设置两个子JAVA_HOME,一个总设置两个子JAVA_HOME:JAVA_HOME6 = C:\Program Files\Java\jdk1.6.0_43JAVA_HOME8 = C:\Progr ...

  9. 关于Collection接口和Map

    Iterable才是Collection的父接口.不是Iterator. Map,SortedMap属于接口类型,不可以new的方式创建对象. HashMap基于哈希表实现Map接口的类,并允许nul ...

  10. Python学习day34-面向对象和网络编程总结

    figure:last-child { margin-bottom: 0.5rem; } #write ol, #write ul { position: relative; } img { max- ...