Android分包MultiDex策略详解

1.分包背景

这里首先介绍下MultiDex的产生背景。

当Android系统安装一个应用的时候,有一步是对Dex进行优化,这个过程有一个专门的工具来处理,叫DexOpt。DexOpt的执行过程是在第一次加载Dex文件的时候执行的。这个过程会生成一个ODEX文件,即Optimised Dex。执行ODex的效率会比直接执行Dex文件的效率要高很多。

但是在早期的Android系统中,DexOpt有一个问题,DexOpt会把每一个类的方法id检索起来,存在一个链表结构里面。但是这个链表的长度是用一个short类型来保存的,导致了方法id的数目不能够超过65536个。当一个项目足够大的时候,显然这个方法数的上限是不够的。尽管在新版本的Android系统中,DexOpt修复了这个问题,但是我们仍然需要对低版本的Android系统做兼容。

为了解决方法数超限的问题,需要将该dex文件拆成两个或多个,为此谷歌官方推出了multidex兼容包,配合AndroidStudio实现了一个APK包含多个dex的功能。

我们在Android开发中,会不断的在App代码里面增加新功能,引入新的类库,如果不加控制的话,那么会碰到编辑器IDE爆出一下错误:

Error:Execution failed for task ':ttt:transformClassesWithDexForDebug'.
com.android.build.api.transform.TransformException: com.android.ide.common.process.ProcessException: java.util.concurrent.ExecutionException: com.android.dex.DexIndexOverflowException: method ID not in [0, 0xffff]: 65536

这个错误是Android应用的对方法总数有限制造成的。Android平台的Java虚拟机Dalvik在执行DEX格式的Java应用程序时,使用原生类型short来索引DEX文件中的方法。这意味着单个DEX文件可被引用的方法总数被限制为65536。通常APK包含一个classes.dex文件,因此Android应用的方法总数不能超过这个数量,这包括Android框架、类库和你自己开发的代码。

这个问题可以通过将一个DEX文件分拆成多个DEX文件解决。

2. 分包策略实现

Gradle 配置:

defaultConfig {
    applicationId "XXX"
    minSdkVersion 14
    targetSdkVersion 23
    multiDexEnabled true
}
.......
dependencies {
  compile 'com.android.support:multidex:1.0.0'
}

在应用的Application 类重写方法:

@Override
protected void attachBaseContext(Context base) {
  super.attachBaseContext(base);
  MultiDex.install(this);
}

3.分包效果说明

经过以上的配置,你的应用已经可以实现多个DEX文件了。当应用构建时,构建工具会分析哪些类必须放在第一个DEX文件,哪些类可以放在附加的DEX文件中。当它创建了第一个DEX文件后,如果有必要会继续创建附加的DEX文件,如classes2.dex, classes3.dex。Multidex的支持类库将被包含在应用的第一个DEX文件中,帮助实现对其它DEX文件的访问。

虽然Google解决了应用总方法数限制的问题,但并不意味着开发者可以任意扩大项目规模。Multidex仍有一些限制:

DEX文件安装到设备的过程非常复杂,如果第二个DEX文件太大,可能导致应用无响应。此时应该使用ProGuard减小DEX文件的大小。

由于Dalvik linearAlloc的Bug,应用可能无法在Android 4.0之前的版本启动,如果你的应用要支持这些版本就要多执行测试。
同样因为Dalvik linearAlloc的限制,如果请求大量内存可能导致崩溃。Dalvik linearAlloc是一个固定大小的缓冲区。在应用的安装过程中,系统会运行一个名为dexopt的程序为该应用在当前机型中运行做准备。dexopt使用LinearAlloc来存储应用的方法信息。Android 2.2和2.3的缓冲区只有5MB,Android 4.x提高到了8MB或16MB。当方法数量过多导致超出缓冲区大小时,会造成dexopt崩溃。

-Multidex构建工具还不支持指定哪些类必须包含在首个DEX文件中,因此可能会导致某些类库(例如某个类库需要从原生代码访问Java代码)无法使用。

4.对开发者的建议

开发者应该避免使用Google Guava这样的类库,它包含了13000多个方法。

尽量使用专为移动应用设计的Lite/Android版本类库,或者使用小类库替换大类库,例如用Google-gson替换Jackson JSON。而对于Google Protocol Buffers这样的数据交换格式,其标准实现会自动生成大量的方法。采用Square Wire的实现则可以很好地解决此问题。

在出现应用分包后低版本手机无法使用,高版本正常使用的问题时,可以考虑检查一下分包的配置是否正确。

总结

以上就是本文关于Android分包MultiDex策略详解的全部内容,希望对大家有所帮助。感兴趣的朋友可以继续参阅本站其他相关内容,如有不足之处,欢迎留言指出。

(0)

相关推荐

  • AndroidStudio利用android-support-multidex解决64k的各种异常

    64k的各种异常 当你的应用程序和库引用达到一定规模,你遇到构建错误显示你的应用已经达到了一个Android应用程序构建体系结构的限制.早期版本的构建系统报告这个错误如下: Conversion to Dalvik format failed: Unable to execute dex: method ID not in [0, 0xffff]: 65536 或者 UNEXPECTED TOP-LEVEL EXCEPTION:  java.lang.IllegalArgumentExcepti

  • Android分包MultiDex策略详解

    1.分包背景 这里首先介绍下MultiDex的产生背景. 当Android系统安装一个应用的时候,有一步是对Dex进行优化,这个过程有一个专门的工具来处理,叫DexOpt.DexOpt的执行过程是在第一次加载Dex文件的时候执行的.这个过程会生成一个ODEX文件,即Optimised Dex.执行ODex的效率会比直接执行Dex文件的效率要高很多. 但是在早期的Android系统中,DexOpt有一个问题,DexOpt会把每一个类的方法id检索起来,存在一个链表结构里面.但是这个链表的长度是用一

  • Android系统对话框使用详解(最详细)

    在实际应用开发中,用到系统对话框中的情况几乎是没有的.按开发流程来说,UI工程师都会给出每一个弹窗的样式,故而在实际开发中都是自定义弹窗的. 即使用到的地方不多,但是我们也是需要了解并且能熟练的运用它,下面为大家奉上各种系统对话框的实现. 目录 一.系统对话框的几种类型与实现 在项目的实际开发中,用到的系统对话框几乎是没有的.原因大概包含以下几点: 样式过于单一,不能满足大部分实际项目中的需求. 对话框的样式会根据手机系统版本的不同而变化.不能达到统一的样式. 能实现的功能过于简单. 在这里先附

  • Android HandlerThread使用方法详解

    Android HandlerThread使用方法详解 HandlerThread 继承自Thread,内部封装了Looper. 首先Handler和HandlerThread的主要区别是:Handler与Activity在同一个线程中,HandlerThread与Activity不在同一个线程,而是别外新的线程中(Handler中不能做耗时的操作). 用法: import android.app.Activity; import android.os.Bundle; import androi

  • Android中menu使用详解

    Menu(菜单)是Android中一定会使用的模块,每个Android项目都会用到Menu来给用户起到选择和导航的作用,提升用户体验,下面通过本文给大家分享android 中menu使用,需要的朋友一起看看吧 很多activity界面中都存在一个菜单栏,就是点击右上角的一个按钮的时候会出现一个下拉列表差不多的东西,这个功能的实现其实只需要下面的两步,每一个activity都可以拥有自己独一无二的menu,具体的格式可以自己进行定义,详细的创建步骤如下 ①在res下的menu中创建file_men

  • Android xml解析实例详解

    Android  xml解析实例详解 实现效果图: XmlActivity package com.Android.xiong.gridlayoutTest; import android.app.Activity; import android.content.res.XmlResourceParser; import android.graphics.Color; import android.graphics.Typeface; import android.os.Bundle; impo

  • Android AOP注解Annotation详解(一)

    Android 注解Annotation 相关文章: Android AOP注解Annotation详解(一) Android AOP之注解处理解释器详解(二) Android AOP 注解详解及简单使用实例(三) Android AOP 等在Android上应用越来越广泛,例如框架ButterKnife,Dagger2,EventBus3等等,这里我自己总结了一个学习路程. - Java的注解Annotation - 注解处理解析器APT(Annotation Processing Tool)

  • Android Tab 控件详解及实例

    Android Tab 控件详解及实例 在桌面应用中Tab控件使用得非常普遍,那么我们经常在Android中也见到以Tab进行布局的客户端.那么Android中的Tab是如何使用的呢? 1.Activity package com.wicresoft.activity; import com.wicresoft.myandroid.R; import android.app.TabActivity; import android.os.Bundle; import android.util.Lo

  • Android canvas drawBitmap方法详解及实例

     Android canvas drawBitmap方法详解及实例 之前自己在自定义view,用到canvas.drawBitmap(Bitmap, SrcRect, DesRect, Paint)的时候,对其中的第2和3个参数的含义含糊不清.看源码函数也没理解,然后看了一些其他的博客加上自己的理解,整理如下.首先,我们看一张图片,今天就要绘制这张图片. 然后将图片用红色的线条分成4个部分,如下: 我们自定义一个View,代码如下: public class PoterDuffLoadingVi

  • Android init.rc文件详解及简单实例

    Android init.rc文件详解 本文主要来自$ANDROID_SOURCE/system/init/readme.txt的翻译. 1 简述 Android init.rc文件由系统第一个启动的init程序解析,此文件由语句组成,主要包含了四种类型的语句:Action,Commands,Services,Options.在init.rc文件中一条语句通常是占据一行.单词之间是通过空格符来相隔的.如果需要在单词内使用空格,那么得使用转义字符"\",如果在一行的末尾有一个反斜杠,那么

  • Android getevent用法实例详解

     Android getevent用法实例详解 最近在测试设备按键的常用命令,感觉这些命令都有的,但就是不知道怎么查找. 翻阅了几篇博文,才发现有一个getevent,就是指这样的命令. 首先需要说明的是getevent命令后面可以带上具体的input设备,列如getevent /dev/iput/event0,这样可以过滤掉一些不用显示的input的设备. 我在之前的使用中,还是有些找不到点子,也是一步一步使用起来的. 首先看-p 选项, -p选项用于输出input设备相关的一些信息,列如,

随机推荐