MultiDex

出现的原因

Android 应用 (APK) 文件包含 Dalvik Executable (DEX) 文件形式的可执行字节码文件,其中包含用来运行您的应用的已编译代码。Dalvik Executable 规范将可在单个 DEX 文件内可引用的方法总数限制在 65,536,其中包括 Android 框架方法、库方法以及您自己代码中的方法。

单个Dex文件中,method个数采用使用原生类型short来索引,即2个字节最多65536个method,field、class的个数也均有此限制。

对于Dex文件,则是将工程所需全部class文件合并且压缩到一个DEX文件期间,也就是使用Dex工具将class文件转化为Dex文件的过程中, 单个Dex文件可被引用的方法总数(自己开发的代码以及所引用的Android框架、类库的代码)被限制为65536。

这就是65536问题的根本来源。

另外还有一种情况,有时候方法数没有达到 65536,并且编译器也正常的完成了编译工作,但是应用在低版本手机安装是异常终止,异常信息如下:

E/dalvikvm: Optimization failed
E/installd: dexopt failed on '/data/dalvik-cache/data@app@com.aprz.test.apk@classes.dex' res = 65433

为什么会出现这种情况呢?其实是这样的,dexopt是一个程序,应用在安装时,系统会通过dexopt 来优化dex文件,在优化的过程中 dexopt 采用一个固定大小的缓冲去来储存应用中的所有方法的信息,这个缓冲区就是 LinearAlloc。LinearAlloc缓冲区在新版本的 Android 系统中其大小时是 8MB 或者 16MB,但是在 Android 2.2和2.3中却只有5MB,当待安装的apk方法数比较多的时候,尽管它没有达到65536的限制,但是它的储存空间仍然有可能超过5MB,这中情况下dexopt程序就会报错。

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

Android 5.0(API 级别 21)之前的平台版本使用 Dalvik 运行时来执行应用代码。默认情况下,Dalvik 限制应用的每个 APK 只能使用单个 classes.dex 字节码文件。想要绕过这个限制,就需要使用Google提供的Dalvik 可执行文件分包支持库

因为Android系统在启动应用时只加载了主dex(Classes.dex),其他的 dex 需要我们在应用启动后进行动态加载安装。

public class MyApplication extends SomeOtherApplication {
  @Override
  protected void attachBaseContext(Context base) {
     super.attachBaseContext(base);
     // 加载其他的dex文件
      // 原理就是:通过反射手动添加其他Dex文件中的class到 ClassLoader 的 pathList字段中,就可以实现类的动态加载
     MultiDex.install(this);
  }
}

这个过程一般只在第一次冷启动应用的时候比较耗时,除了要抽取其他的 dex 文件,Dalvik 虚拟机还会使用 dex2oat 将 dex 文件优化成 odex 文件,将生成的文件放在手机的data/dalvik-cache目录下,便于以后使用。以后再次运行时,因为不用再次生成 odex,所以运行速度很快。

Android 5.0 及更高版本的 Dalvik 可执行文件分包支持

Android 5.0(API 级别 21)及更高版本使用名为 ART 的运行时,后者原生支持从 APK 文件加载多个 DEX 文件。ART 在应用安装时执行预编译,扫描 classesN.dex 文件,并将它们编译成单个 .oat 文件,供 Android 设备执行。因此,如果您的 minSdkVersion 为 21 或更高值,则不需要 Dalvik 可执行文件分包支持库。只需要在模块级 build.gradle 文件中将 multiDexEnabled 设置为 true,如此处所示:

android {
    defaultConfig {
        ...
        minSdkVersion 21 
        targetSdkVersion 28
        multiDexEnabled true
    }
    ...
}

MultiDex.install 带来的问题

当我们使用了分包支持库之后,在运行app时可能会出现这样的错误

java.lang.NoClassDefFoundError

出现这个问题的原因是:在应用启动期间,需要该类,但是这个类不在 MainDex 中,所以解决方案就是将这个类放到 MainDex 中。

具体可以参考官方文档: 声明主 DEX 文件中需要的类

dexopt failed

dalvik的dexopt程序分配一块内存来统计你的app的dex里面的classes的信息,由于classes太多方法太多超过这个linearAlloc 的限制 。

解决方案就是减少 dex 的大小。

android.applicationVariants.all {
    variant ->
        dex.doFirst{
            dex->
            if (dex.additionalParameters == null) {
                dex.additionalParameters = []
            }
                dex.additionalParameters += '--set-max-idx-number=48000'
       }
}
启动过程中 ANR

启动期间在设备数据分区中安装 DEX 文件的过程相当复杂,如果辅助 DEX 文件较大,可能会导致应用无响应 (ANR) 错误。在此情况下,您应该通过 ProGuard 应用代码压缩以尽量减小 DEX 文件的大小,并移除未使用的那部分代码。

这个是官方给出的建议,但是显然不太合适中国程序员国情,所以需要使用别的方案:

这两个实现其实差不多,都是提供了一种避免在其他Dex文件未加载完成时,造成的ClassNotFoundException的手段。

分包后 MainDex 仍然爆掉

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值