google为什么要引入MultiDex?

dex指令是用16位寄存器来保存dex中的方法数,所以限制了在apk

中最大的方法数为65535,当超过这个最大值在编译的时候会报

方法数超标的错误。

如何引入MultiDex?

1.修改gradle脚本来产生多dex。
2.修改manifest 使用MulitDexApplication。

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

  1. android {
  2. compileSdkVersion 21
  3. buildToolsVersion "21.1.0"
  4. defaultConfig {
  5. ...
  6. minSdkVersion 14
  7. targetSdkVersion 21
  8. ...
  9. // Enabling multidex support.
  10. multiDexEnabled true
  11. }
  12. ...
  13. }
  14. dependencies {
  15. compile 'com.android.support:multidex:1.0.0'
  16. }

步骤2. manifest声明修改

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <manifest xmlns:android="http://schemas.android.com/apk/res/android"
  3. package="com.example.android.multidex.myapplication">
  4. <application
  5. ...
  6. android:name="android.support.multidex.MultiDexApplication">
  7. ...
  8. </application>
  9. </manifest>

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

  1. @Override
  2. protected void attachBaseContext(Context base) {
  3. super.attachBaseContext(base);
  4. MultiDex.install(this);
  5. }

引入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脚本如下:

  1. android.applicationVariants.all {
  2. variant ->
  3. dex.doFirst{
  4. dex->
  5. if (dex.additionalParameters == null) {
  6. dex.additionalParameters = []
  7. }
  8. dex.additionalParameters += '--set-max-idx-number=48000'
  9. }
  10. }

--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到底有多坑的更多相关文章

  1. 【转】其实你不知道MultiDex到底有多坑

    遭遇MultiDex 愉快地写着Android代码的总悟君往工程里引入了一个默默无闻的jar然后Run了一下, 经过漫长的等待AndroidStudio构建失败了. 于是带着疑惑查看错误信息. UNE ...

  2. STM32 硬件I2C 到底是不是个坑?

    /** ****************************************************************************** * @author    Maox ...

  3. Android Studio MultiDex 分包碰到的坑

    前天准备发包了,测试完毕,打好正式签名包,装到手机上,运行不起来. 网上查了大量资料,都没有解决方案. log显示如下: 04-26 10:07:57.727 1538-1538/? I/MultiD ...

  4. android MultiDex multidex原理原理下遇见的N个深坑(二)

    android MultiDex 原理下遇见的N个深坑(二) 这是在一个论坛看到的问题,其实你不知道MultiDex到底有多坑. 不了解的可以先看上篇文章:android MultiDex multi ...

  5. Android 性能优化的方面方面都在这儿

    又到周六了,鸿洋的不定期的周六放送又来了~~这次来谈谈性能优化吧.大家在工作中或多或少都会拿自家的应用和竞品app做比对,不可避免的需要做一些app性能优化的活.很多时候可能是策略上的调整,不过还是有 ...

  6. Objects.equals有坑

    前言 最近review别人代码的时候,发现有个同事,在某个业务场景下,使用Objects.equals方法判断两个值相等时,返回了跟预期不一致的结果,引起了我的兴趣. 原本以为判断结果会返回true的 ...

  7. swift 键盘属性与事件

    1.键盘的类型 textField1.keyboardType = UIKeyboardType.default //系统默认的虚拟键盘 textField1.keyboardType = UIKey ...

  8. JS魔法堂:属性、特性,傻傻分不清楚

    一.前言 或许你和我一样都曾经被下面的代码所困扰 var el = document.getElementById('dummy'); el.hello = "test"; con ...

  9. NSDictionary初始化,使用@{}方法,插入nil时会报空指针异常

    由于今天在NSDictionary初始化的时候出现Crash异常,故记录一下,避免下次再犯. 在Objective-C中,NSDictionary初始化的方法有很多种 方法1: [NSDictiona ...

随机推荐

  1. python file模块 替换输入内容脚本

    root@python-10:/home/liujianzuo/python/test# ls passwd rc.local test1 root@python-10:/home/liujianzu ...

  2. mysql qps tps计算

    Information from web QPS (Query per second) (每秒查询量)TPS(Transaction per second) (每秒事务量,如果是InnoDB会显示,没 ...

  3. js web实现移动端触控

    // 触摸事件 $(".m_l_i_l a").on("touchstart", function(){ $(this).css("color&quo ...

  4. TCP/IP详解--连接状态变迁图CLOSE_WAIT

    终止一个连接要经过4次握手.这由TCP的半关闭(half-close)造成的.既然一个TCP连接是全双工(即数据在两个方向上能同时传递,可理解为两个方向相反的独立通道),因此每个方向必须单独地进行关闭 ...

  5. Velocity(4)——大小写

    Velocity是大小写敏感的.但是为了减少使用者的错误,某些地方存在特殊情况. 比如$foo.bar,首先会被检查是否能匹 配$foo.getbar(),再被检查是否能匹配$foo.getBar() ...

  6. boost源码剖析----boost::any

    boost源码剖析----boost::any 有的时候我们需要有一个万能类型来进行一些操作,这时候boost::any就派上用场了. boost::Any testInt(10); int val ...

  7. MVC 4 异步编程简化了

    MVC 3 异步编程好麻烦,需要使用异步控制器,一个Action需要拆成两个,很不方便.MVC3的好处是,只需要.NET Framework 4.0就能运行 MVC 4 之后只需要使用async和aw ...

  8. 跟着8张思维导图学习javascript

    学习的道路就是要不断的总结归纳,好记性不如烂笔头,so,下面将po出8张javascript相关的思维导图. 思维导图小tips:思维导图又叫心智图,是表达发射性思维的有效的图形思维工具 ,它简单却又 ...

  9. Java虚拟机学习(4):对象内存分配与回收

    对象优先在Eden上分配 大多数情况下,对象优先在新生代Eden区域中分配.当Eden内存区域没有足够的空间进行分配时,虚拟机将触发一次 Minor GC(新生代GC).Minor GC期间虚拟机将E ...

  10. 【转】Struts1.x系列教程(2):简单的数据验证

    转载地址:http://www.blogjava.net/nokiaguy/archive/2009/01/archive/2009/01/13/251197.html 简单验证从本质上说就是在服务端 ...