Multidex记录二:缺陷&解决

个人博客地址 http://dandanlove.com/

Multidex记录一:介绍和使用
Multidex记录二:缺陷&解决
Multidex记录三:源码解析

记录Multidex缺陷&解决

为什么要用记录呢,因为我从开始接触Android时我们的项目就在65535的边缘。不久Google就出了multidex的解决方案。我们也已经接入multidex好多年,但我自己还没有接入,所以本博文只是作者自己对multidex接入中产生的问题以及解决方案做理解和记录。

Multidex的缺陷

Multidex介绍和使用 中已经说了一部分multidex的局限性:

  • 1、在冷启动时因为需要安装DEX文件,如果DEX文件过大时,处理时间过长,很容易引发ANR(Application Not Responding);
  • 2、采用MultiDex方案的应用可能不能在低于Android 4.0 (API level 14) 机器上启动,这个主要是因为Dalvik linearAlloc的一个bug(问题 22586) ;
  • 3、采用MultiDex方案的应用因为需要申请一个很大的内存,在运行时可能导致程序的崩溃,这个主要是因为Dalvik linearAlloc 的一个限制问题 78035
    ,这个限制在 Android 4.0 (API level 14)已经增加了, 应用也有可能在低于 Android 5.0 (API level 21)版本的机器上触发这个限制。

Google官方给解决办法就是混淆、混淆

Dalvik LinearAlloc

局限2和3都与Dalvik LinearAlloc,我们先来看一下Dalvik LinearAlloc是什么:

线性内存分配器LinearAlloc的目的在于简单、快速地分配只写一次(write-once)的内存(即分配并完成初始化写入后一般不会再改变,保持只读性质)

LinearAlloc它主要用来管理Dalvik中类加载时的内存,因为类加载后通常是只读属性,而不需要去改变且在程序的整个运行周期都是有效的,同时它还有共享的特性,一个应用加载后其它进程可以共享使用这些已加载的类从而加快程序的启动和运行速度。

在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崩溃。

LinearAlloc解决方法

这个问题实质上是dex过大的问题,因为我们使用的multidex,dx命令就已经支持:--multi-dex 参数来直接自动分包。

我们查看dx命令:

dx.png

multidex相关参数说明:

  • --multi-dex:多 dex 打包的开关
  • --main-dex-list=<file>:参数是一个类列表的文件,在该文件中的类会被打包在第一个 dex 中
  • --minimal-main-dex:只有在--main-dex-list 文件中指定的类被打包在第一个 dex,其余的都在第二个 dex 文件中。

发现并没有控制dex中方法数的参数,那么继续查看dx的源码,我们找到一个maxNumberOfIdxPerDex变量用来指定dex的最大方法数。

//65536
private int maxNumberOfIdxPerDex = DexFormat.MAX_MEMBER_IDX + 1;

同时又一个隐藏的--set-max-idx-number参数可以用来修改maxNumberOfIdxPerDex 的值:

--set-max-idx-number=.png

我们修改项目的build.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。为了避免2.3机型runtime 的linearAlloclimit ,最好保持每一个dex体积<4M ,刚才的的value<=48000

Application Not Responding解决:

Multidex的安装是比较耗时的,所以如果放在主线程中就会产生ANR。

目前有两类解决办法:

放在异步线程;
放在其他进程(我们使用的是第二种,下边详细讲解);

异步线程执`MultiDex.install

最有名的是美团的方案:精简主dex+异步加载secondary.dex 。对异步化执行速度的不确定性,他们的解决方案是重写Instrumentation execStartActivity 方法,hook跳转Activity的总入口做判断,如果当前secondary.dex 还没有加载完成,就弹一个loading Activity等待加载完成,如果已经加载完成那最好不过了。

局限性:第一个dex必须包含所有可能启动之后ClassLoader的类,不然一定会产生NoClassDefFoundError异常。Application的启动入口有多重,点击桌面icon只不过是其中的一种,而且有些时候启动Application不一定会打开Activity。

放在其他进程

微信团队的方案:
流程图:


泡在网上的日子
  • 对现有代码改动量最小;
  • 该方案不关注Application被哪个组件启动。Activity ,Service ,Receiver ,ContentProvider 都满足(与美团方案都相同的问题,假如打开的不是Activity。这个时候弹出一个过渡的Activity就非常尴尬);
  • 该方案不限制 Application ,Activity ,Service ,Receiver ,ContentProvider 继续新增业务;

实现代码:
泡在网上的日子:其实你不知道MultiDex到底有多坑

单独说一下waitForDexopt这个方法,这里设置的10s(Honeycomb之前20s)的轮询之后执行了MultiDex.install。此时在mini进程中Multidex可能还未完成安装(我们项目目前一共3个dex,Multidex的安装耗时大概20s)。

public void waitForDexopt(Context base) {
    Intent intent = new Intent();
    ComponentName componentName = new
            ComponentName( "com.zongwu", LoadResActivity.class.getName());
    intent.setComponent(componentName);
    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    base.startActivity(intent);
    long startWait = System.currentTimeMillis ();
    long waitTime = 10 * 1000 ;
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB_MR1 ) {
        waitTime = 20 * 1000 ;//实测发现某些场景下有些2.3版本有可能10s都不能完成optdex
    }
    while (needWait(base)) {
        try {
            long nowWait = System.currentTimeMillis() - startWait;
            LogUtils.d("loadDex" , "wait ms :" + nowWait);
            if (nowWait >= waitTime) {
                return;
            }
            Thread.sleep(200 );
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

当启动:mini进程后,主进程就会切换为后台进程所以不存在ANR的问题。我们可以一直轮询needWait,直到Multidex加载完成。

public void waitForDexopt(Context base) {
    /***部分代码省略***/
    while (needWait(base)) {
        try {
            //long nowWait = System.currentTimeMillis() - startWait;
            //LogUtils.d("loadDex" , "wait ms :" + nowWait);
            //if (nowWait >= waitTime) {
            //    return;
            //}
            Thread.sleep(200 );
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

参考资料:

泡在网上的日子:其实你不知道MultiDex到底有多坑
尼古拉斯_赵四:Android关于Dex拆分(MultiDex)技术详解

文章到这里就全部讲述完啦,若有其他需要交流的可以留言哦

想阅读作者的更多文章,可以查看我 个人博客 和公共号:

振兴书城

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,298评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,701评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 107,078评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,687评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,018评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,410评论 1 211
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,729评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,412评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,124评论 1 239
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,379评论 2 242
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,903评论 1 257
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,268评论 2 251
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,894评论 3 233
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,014评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,770评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,435评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,312评论 2 260