• MultiDex到底有多坑


    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

  • 相关阅读:
    创业之死:97%的创业失败是因为…… . 分类: 项目管理 2014-06-18 17:56 315人阅读 评论(1) 收藏
    linux下的APK反编译软件及过程介绍 . 分类: arm-linux-Ubuntu 2014-06-18 17:51 400人阅读 评论(0) 收藏
    g++基本用法 分类: arm-linux-Ubuntu 2014-06-18 17:50 414人阅读 评论(0) 收藏
    更改 vc6 各窗口字体 (zz)
    程序员编程技术迅速提高终极攻略 (zz)
    A browser for WinCE/Windows base WebKit. (zz)
    输入一个十进制的数到dx_ax,然后十六进制转十进制输出
    汇编判断语句
    用结构体指针存储数据__正序_逆序下的输入
    二叉排序树
  • 原文地址:https://www.cnblogs.com/lzl-sml/p/5216861.html
Copyright © 2020-2023  润新知