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. 基于jdk8的格式化时间方法

    背景 jdk8之前,java使用Date表示时间,在做时间的格式化时,通常使用SimpleDateFormat,但是SimpleDateFormat是非线程安全的,在写代码时通常要将之定义为局部变量或 ...

  2. CSS三大特性之继承性

    1.并不是所有的属性都可以继承,只有以color/font/text/line开头的属性 才可以继承. 2.在CSS的继承中,不仅仅是儿子可以继承,只要是后代都可以继承. 3.继承中的特殊性 3.1  ...

  3. virtualbox导入winXP系统OVA文件重启

    1,开启虚拟机 2,按f8进入安全模式,然后修改注册表: HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Processor HKEY_LOC ...

  4. .net Cache的用法

    HttpContext.Current.Cache 使用方法 .net Cache 怎么使用 /// <summary>        /// 简单创建/修改Cache,前提是这个值是字符 ...

  5. mysql on windows的安装

    1.去官网下载合适的压缩包 网址:https://dev.mysql.com/downloads/file/?id=476233 (拉到最下面点击 No thanks,just start my do ...

  6. springboot实现转发和重定向

    1.转发     方式一:使用 "forword" 关键字(不是指java关键字),注意:类的注解不能使用@RestController 要用@Controller @Reques ...

  7. (转)剖析Linux文件编码的查看及修改

    Linux文件编码的查看和修改都有不止一种做法,如果你需要在Linux中操作windows下的文件,那么很可能会经常遇到文件编码转换的问题,如何进行这项工作,也应该是经常工作在双系统下的操作者的必须掌 ...

  8. Linux 启动出现 busybox vx.x.xx built-in shell 的问题

    可能是磁盘检测错误,尤其出现在未安全关机或者磁盘损坏之后. 解决办法: 1.在选择启动项目时,选中第一项,如: ubuntu 8.04kernl.2.6.22-16-generic 2.按E 进入编辑 ...

  9. csdn阅读更多自动展开插件

    点击获取 当然也可以自己写脚本.写js.

  10. <input> type 属性

    单行文本域 语法格式:<input  type = “text” 属性 = “值” /> 常用属性 1  name:文本框的名字.命名规则是:可以包含字母.数字.下划线,只能以字母开头. ...