MultiDex到底有多坑
google为什么要引入MultiDex?
dex指令是用16位寄存器来保存dex中的方法数,所以限制了在apk
中最大的方法数为65535,当超过这个最大值在编译的时候会报
方法数超标的错误。
如何引入MultiDex?
1.修改gradle脚本来产生多dex。
2.修改manifest 使用MulitDexApplication。
步骤1.在gradle脚本里写上:
android {compileSdkVersion 21buildToolsVersion "21.1.0"defaultConfig {...minSdkVersion 14targetSdkVersion 21...// Enabling multidex support.multiDexEnabled true}...}dependencies {compile 'com.android.support:multidex:1.0.0'}
步骤2. manifest声明修改
<?xml version="1.0" encoding="utf-8"?><manifest xmlns:android="http://schemas.android.com/apk/res/android"package="com.example.android.multidex.myapplication"><application...android:name="android.support.multidex.MultiDexApplication">...</application></manifest>
如果有自己的Application,继承MulitDexApplication。如果当前代码已经继承自其它Application没办法修改那也行,就重写 Application的attachBaseContext()这个方法。
@Overrideprotected void attachBaseContext(Context base) {super.attachBaseContext(base);MultiDex.install(this);}
引入multiDex后会碰到的亢
1.在应用安装到手机上的时候dex文件的安装是复杂的(complex)有可能会因为第二个dex文件太大导致ANR。请用proguard优化你的代码。
2.使用了mulitDex的App有可能在4.0(api level 14)以前的机器上无法启动,因为Dalvik linearAlloc bug(Issue 22586) 。请多多测试自祈多福。用proguard优化你的代码将减少该bug几率。
3.使用了mulitDex的App在runtime期间有可能因为Dalvik linearAlloc limit (Issue 78035) 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
MultiDex到底有多坑的更多相关文章
- 【转】其实你不知道MultiDex到底有多坑
遭遇MultiDex 愉快地写着Android代码的总悟君往工程里引入了一个默默无闻的jar然后Run了一下, 经过漫长的等待AndroidStudio构建失败了. 于是带着疑惑查看错误信息. UNE ...
- STM32 硬件I2C 到底是不是个坑?
/** ****************************************************************************** * @author Maox ...
- Android Studio MultiDex 分包碰到的坑
前天准备发包了,测试完毕,打好正式签名包,装到手机上,运行不起来. 网上查了大量资料,都没有解决方案. log显示如下: 04-26 10:07:57.727 1538-1538/? I/MultiD ...
- android MultiDex multidex原理原理下遇见的N个深坑(二)
android MultiDex 原理下遇见的N个深坑(二) 这是在一个论坛看到的问题,其实你不知道MultiDex到底有多坑. 不了解的可以先看上篇文章:android MultiDex multi ...
- Android 性能优化的方面方面都在这儿
又到周六了,鸿洋的不定期的周六放送又来了~~这次来谈谈性能优化吧.大家在工作中或多或少都会拿自家的应用和竞品app做比对,不可避免的需要做一些app性能优化的活.很多时候可能是策略上的调整,不过还是有 ...
- Objects.equals有坑
前言 最近review别人代码的时候,发现有个同事,在某个业务场景下,使用Objects.equals方法判断两个值相等时,返回了跟预期不一致的结果,引起了我的兴趣. 原本以为判断结果会返回true的 ...
- swift 键盘属性与事件
1.键盘的类型 textField1.keyboardType = UIKeyboardType.default //系统默认的虚拟键盘 textField1.keyboardType = UIKey ...
- JS魔法堂:属性、特性,傻傻分不清楚
一.前言 或许你和我一样都曾经被下面的代码所困扰 var el = document.getElementById('dummy'); el.hello = "test"; con ...
- NSDictionary初始化,使用@{}方法,插入nil时会报空指针异常
由于今天在NSDictionary初始化的时候出现Crash异常,故记录一下,避免下次再犯. 在Objective-C中,NSDictionary初始化的方法有很多种 方法1: [NSDictiona ...
随机推荐
- Java学习-043-获取文件在目录中的路径
我们在日常的电脑使用中,经常需要在当前目录或当期目录及其子目录中查找文件,并获取相应的文件路径名.在我们的自动化测试中,也经常需要确认文件在目录中是否成功生成或已存在,因而我写了一个小方法来实现. 获 ...
- iOS应用架构谈 网络层设计方案
网络层在一个App中也是一个不可缺少的部分,工程师们在网络层能够发挥的空间也比较大.另外,苹果对网络请求部分已经做了很好的封装,业界的AFNetworking也被广泛使用.其它的ASIHttpRequ ...
- float属性
float属性介绍 float给人一种捉摸不透的感觉,不过可以依照浏览器的解析机制(根据HTML文档,从上往下解析),对float属性了解一二.float有四种值:none/left/right/in ...
- [Android Tips] 17. Check APK Sign Info
$ jarsigner -verify -verbose -certs <apk file path> 查看 keystore $ keytool -list -keystore debu ...
- 湖大OJ-实验E----可判定的DFA的空问题
实验E----可判定的DFA的空问题 Time Limit: 1000ms, Special Time Limit:2500ms, Memory Limit:32768KB Total submit ...
- Excel命名区域的创建、修改、删除、命名
因工作需要,需要用到 Excel实现多级下拉列表,这是预备知识! 链接地址:http://wenku.baidu.com/link?url=vHkAo25IXo6mabms-Jv0m3T7BRfAMg ...
- JS中同名函数有效执行顺序
html中如果出现函数同名时:如果有多个外部引入的js文件,例如a.js和b.js(引入顺序假定是a.js,然后是b.js),同时html中本身也有内部的js.那么针对 出现函数名一样的情况时,无论他 ...
- 【001:ubuntu下搭建ESP8266开发环境--编辑 编译 下载】
系统环境:ubuntu 16.04 TLS 64BIT 编辑器: Eclipse CDT 版本 编译器:xtensa-lx106-elf 交叉编译工具链 下载工具:esptool.py pyseria ...
- Eclipse中实现JAVA代码的自动提示功能
1.打开Eclipse,在.出现时进行代码提示换成任意字母+.出现时的代码提示了(.abcdefghijklmnopqrstuvwxyz):
- 关于webservice大数据量传输时的压缩和解压缩
当访问WebSerivice时,如果数据量很大,传输数据时就会很慢.为了提高速度,我们就会想到对数据进行压缩.首先我们来分析一下. 当在webserice中传输数据时,一般都采用Dataset进行数据 ...