Android MultiDex的用法记录

1、Multidex的产生

在android5.0之前,每一个android应用中只会含有一个dex文件,但是因为Android系统本身的BUG,使得这个dex的方法数量被限制在65535之内,这就是著名的“64K(64*1024)”事件。为了解决这个问题,Google官方推出了这个类似于补丁一样的support-library。关于这个库的详细使用,可以参考官方文档,当然使用起来也会有些坑的,美团填坑记或者这位老兄。使用这个库后,我们的APP不再只会仅有一个dex文件,可能会产生多个dex文件,这样就避免了64K问题。

2.如何引入MultiDex?

2.1.修改gradle脚本来产生多dex。

2.2.修改manifest 使用MulitDexApplication。

步骤1.在gradle脚本里写上:

android{

compileSdkVersion21

buildToolsVersion”21.1.0″

defaultConfig{

minSdkVersion14

targetSdkVersion21

// Enabling multidex support.

multiDexEnabledtrue

}

}

dependencies{

compile’com.android.support:multidex:1.0.0′

}

步骤2. manifest声明修改

package=”com.example.android.multidex.myapplication”>

android:name=”android.support.multidex.MultiDexApplication”>

a . 如果没有自己的Application,则上面的manifest设置已经够了,

b.  如果有自己的Application,继承MulitDexApplication。如果当前代码已经继承自其它Application没办法修改那也行,就重写 Application的attachBaseContext()这个方法。

@Override

protectedvoidattachBaseContext(Contextbase){

super.attachBaseContext(base);

MultiDex.install(this);

}

引入multiDex后会碰到的坑

1.在应用安装到手机上的时候dex文件的安装是复杂的(complex)有可能会因为第二个dex文件太大导致ANR(Application not Response应用无响应)。请用proguard优化你的代码。

2.使用了mulitDex的App有可能在4.0(api level 14)以前的机器上无法启动,因为Dalvik linearAlloc bug(Issue22586) 。请多多测试自祈多福。用proguard优化你的代码将减少该bug几率。

3.使用了mulitDex的App在runtime期间有可能因为Dalvik linearAlloc limit (Issue78035) Crash。该内存分配限制在 4.0版本被增大,但是5.0以下的机器上的Apps依然会存在这个限制。

4.主dex被dalvik虚拟机执行时候,哪些类必须在主dex文件里面这个问题比较复杂。build tools 可以搞定这个问题。但是如果你代码存在反射和native的调用也不保证100%正确。

INSTALL_FAILED_DEXOPT 解决方案:

apk是一个zip压缩包,dalvik每次加载apk都要从中解压出class.dex文件,加载过程还涉及到dex的classes需要的杂七杂八的依赖库的加载,真耗时间。于是Android决定优化一下这个问题,在app安装到手机之后,系统运行dexopt程序对dex进行优化,将dex的依赖库文件和一些辅助数据打包成odex文件。存放在cache/dalvik_cache目录下。保存格式为apk路径 @ apk名 @ classes.dex。这样以空间换时间大大缩短读取/加载dex文件的过程。

那刚才那个bug是啥问题呢,原来dexopt程序的dalvik分配一块内存来统计你的app的dex里面的classes的信息,由于classes太多方法太多超过这个linearAlloc 的限制 。那减小dex的大小就可以咯。

gradle脚本如下:

android.applicationVariants.all{

variant->

dex.doFirst{

dex->

if(dex.additionalParameters==null){

dex.additionalParameters=[]

}

dex.additionalParameters+=’–set-max-idx-number=48000′

}

}

–set-max-idx-number= 用于控制每一个dex的最大方法个数,写小一点可以产生好几个dex。 踩过更多坑的FB的工程师表示这个linearAlloc的限制不仅仅在安装时候的dexopt程序里7,还在你的app的dalvik rumtime里。(很显然啊dvk vm的宿主进程fork自于同一个母体啊)。为了表示对这个坑的不满以及对Google的产品表示遗憾,FB工程师Read The Fucking Source Code找到了一个hack方案。这个linearAlloc的size定义在c层而且是一个全局变量,他们通过对结构体的size的计算成功覆盖了该值的内容,这里要特别感谢C语言的指针和内存的设计。C的世界里,You Are The King of This World。当然实际情况是大部分用户用这把利刃割伤了自己

首次安装运行ANR问题

于是发现 是 install dex + dexopt 时间太长!

梳理流程:

安装完app点击图标之后,系统木有发现对应的process,于是从该apk抽取classes.dex(主dex) 加载,触发 一次dexopt。

App 的laucherActivity准备启动 ,触发Application启动,

Application的 onattach()方法调用,这时候MultiDex.install()调用,classes2.dex 被install,再次触发dexopt。

然后Applicaition onCreate()执行。

然后 launcher Activity真的起来了。

这些必须在5s内完成不然就ANR给你看!

问题到现在变成了:既希望在Application的attachContext()方法里同步加载secondary.dex,又不希望卡住UI线程。如果思路限制在线程异步化上,确实不可能实现。于是发现了微信开发团队的这篇文章。该文章介绍了关于这一问题 FB/QQ/微信的解决方案。FB的解决思路特别赞,让Launcher Activity在另外一个进程启动!当然这个Launcher Activity就是用来load dex 的 ,load完成就启动Main Activity。

微信这篇文章给出了一个非常重要的观点:安装完成之后第一次启动时,是secondary.dex的dexopt花费了更多的时间。认识到这点非常重要,使得问题又转化为:在不阻塞UI线程的前提下,完成dexopt,以后都不需要再次dexopt,所以可以在UI线程install dex 了!文章最后给了一个对FB方案的改进版。

仔细读完感觉完全可行。

1.对现有代码改动量最小。

2.该方案不关注Application被哪个组件启动。Activity ,Service ,Receiver ,ContentProvider 都满足。(有个问题要说明:如细心网友指出的那样,新安装还未启动但是收到Receiver的场景下,会导致Load界面出现。这个场景实际出现几率比较少,且仅出现一次。可以接受。)

3.该方案不限制 Application ,Activity ,Service ,Receiver ,ContentProvider 继续新增业务。

主要的原理:application启动了LoadDexActivity之后,自身不再是前台进程所以怎么hold 线程都不会ANR

参考链接:http://blog.zongwu233.com/the-touble-of-multidex/?from=groupmessage&isappinstalled=0

    原文作者:PengboGai
    原文地址: https://www.jianshu.com/p/cdabc62316ba
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞