(转)美团Android DEX自动拆包及动态加载简介

来源:互联网 发布:osek网络管理 编辑:程序博客网 时间:2024/06/03 23:00

原文链接:http://tech.meituan.com/mt-android-auto-split-dex.html
概述
作为一个android开发者,在开发应用时,随着业务规模发展到一定程度,不断地加入新功能、添加新的类库,代码在急剧的膨胀,相应的apk包的大小也急剧增加, 那么终有一天,你会不幸遇到这个错误:

生成的apk在android 2.3或之前的机器上无法安装,提示INSTALL_FAILED_DEXOPT
方法数量过多,编译时出错,提示:
Conversion to Dalvik format failed:Unable to execute dex: method ID not in [0, 0xffff]: 65536
而问题产生的具体原因如下:

无法安装(Android 2.3 INSTALL_FAILED_DEXOPT)问题,是由dexopt的LinearAlloc限制引起的,在Android版本不同分别经历了4M/5M/8M/16M限制,目前主流4.2.x系统上可能都已到16M, 在Gingerbread或者以下系统LinearAllocHdr分配空间只有5M大小的, 高于Gingerbread的系统提升到了8M。Dalvik linearAlloc是一个固定大小的缓冲区。在应用的安装过程中,系统会运行一个名为dexopt的程序为该应用在当前机型中运行做准备。dexopt使用LinearAlloc来存储应用的方法信息。Android 2.2和2.3的缓冲区只有5MB,Android 4.x提高到了8MB或16MB。当方法数量过多导致超出缓冲区大小时,会造成dexopt崩溃。
超过最大方法数限制的问题,是由于DEX文件格式限制,一个DEX文件中method个数采用使用原生类型short来索引文件中的方法,也就是4个字节共计最多表达65536个method,field/class的个数也均有此限制。对于DEX文件,则是将工程所需全部class文件合并且压缩到一个DEX文件期间,也就是Android打包的DEX过程中, 单个DEX文件可被引用的方法总数(自己开发的代码以及所引用的Android框架、类库的代码)被限制为65536;
插件化? MultiDex?
解决这个问题,一般有下面几种方案,一种方案是加大Proguard的力度来减小DEX的大小和方法数,但这是治标不治本的方案,随着业务代码的添加,方法数终究会到达这个限制,一种比较流行的方案是插件化方案,另外一种是采用google提供的MultiDex方案,以及google在推出MultiDex之前Android Developers博客介绍的通过自定义类加载过程, 再就是Facebook推出的为Android应用开发的Dalvik补丁, 但facebook博客里写的不是很详细;我们在插件化方案上也做了探索和尝试,发现部署插件化方案,首先需要梳理和修改各个业务线的代码,使之解耦,改动的面和量比较巨大,通过一定的探讨和分析,我们认为对我们目前来说采用MultiDex方案更靠谱一些,这样我们可以快速和简洁的对代码进行拆分,同时代码改动也在可以接受的范围内; 这样我们采用了google提供的MultiDex方式进行了开发。

插件化方案在业内有不同的实现原理,这里不再一一列举,这里只列举下Google为构建超过65K方法数的应用提供官方支持的方案:MultiDex。

首先使用Android SDK Manager升级到最新的Android SDK Build Tools和Android Support Library。然后进行以下两步操作:

1.修改Gradle配置文件,启用MultiDex并包含MultiDex支持:

android {
compileSdkVersion 21 buildToolsVersion “21.1.0”

defaultConfig {    ...    minSdkVersion 14    targetSdkVersion 21    ...    // Enabling MultiDex support.    MultiDexEnabled true    }    ...}dependencies { compile 'com.android.support:MultiDex:1.0.0'

}
2.让应用支持多DEX文件。在官方文档中描述了三种可选方法:

在AndroidManifest.xml的application中声明android.support.MultiDex.MultiDexApplication;
如果你已经有自己的Application类,让其继承MultiDexApplication;
如果你的Application类已经继承自其它类,你不想/能修改它,那么可以重写attachBaseContext()方法:

@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
MultiDex.install(this);
}
并在Manifest中添加以下声明:

0 0