个人博客地址 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命令:

multidex相关参数说明:

  • –multi-dex:多 dex 打包的开关
  • –main-dex-list=:参数是一个类列表的文件,在该文件中的类会被打包在第一个 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 的值:

我们修改项目的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 = newComponentName( "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)技术详解

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

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

Multidex记录二:缺陷解决相关推荐

  1. Multidex记录三:源码解析

    个人博客地址 http://dandanlove.com/ Multidex记录一:介绍和使用 Multidex记录二:缺陷&解决 Multidex记录三:源码解析 记录Multidex源码解 ...

  2. Multidex记录一:介绍和使用

    个人博客地址 http://dandanlove.com/ Multidex记录一:介绍和使用 Multidex记录二:缺陷&解决 Multidex记录三:源码解析 记录Multidex介绍和 ...

  3. 单机游戏计时器防作弊解决方案[转],以及缺陷解决办法

    一.作弊方法描述 一些游戏会在某些情况下让玩家等待一段时间,例如candy crush中游戏失败会消耗一点体力,体力不满时,会每隔30分钟涨一点.体力耗尽时就无法再进行游戏,此时玩家可以修改系统时间, ...

  4. 生产中NFS案例记录---写入权限解决过程

        生产中NFS案例记录---写入权限解决过程 NFS配置要求: 1. 将oracle文件写入到NFS Server端,注意权限要与oracle端一致. 2. Oracle端目录文件所属用户为or ...

  5. Android动画学习记录二(属性动画、估值器和插值器)

    Android动画学习记录二(属性动画.估值期和插值器) Android动画学习记录二(属性动画.估值期和插值器) Android动画学习记录二(属性动画.估值期和插值器) 一.补间动画缺陷 二.属性 ...

  6. Multidex(二)之Dex预加载优化

    Multidex(二)之Dex预加载优化 https://www.jianshu.com/p/2891599511ff 转载于:https://www.cnblogs.com/tc310/p/1024 ...

  7. 记录一次解决404问题

    记录一次解决404问题 参考文章: (1)记录一次解决404问题 (2)https://www.cnblogs.com/xiashiwendao/p/8903771.html 备忘一下.

  8. MySQL学习记录 (二) ----- SQL数据查询语句(DQL)

    相关文章: <MySQL学习记录 (一) ----- 有关数据库的基本概念和MySQL常用命令> <MySQL学习记录 (二) ----- SQL数据查询语句(DQL)> &l ...

  9. Idea(二) 解决IDEA卡顿问题及相关基本配置

    Idea(二) 解决IDEA卡顿问题及相关基本配置 参考文章: (1)Idea(二) 解决IDEA卡顿问题及相关基本配置 (2)https://www.cnblogs.com/zhenghengbin ...

最新文章

  1. 【最全干货】从SGD到NadaMax,十种机器学习优化算法原理及实现
  2. Boost:变量类型boost :: any的单元测试
  3. Session的实现与存储
  4. linux内核 header.s,arm架构的linux内核中,clrex指令的作用是什么
  5. 超平面(hyperplane)
  6. SQL调优和性能监控利器SQL Monitor
  7. 90. 子集 II(中等 回溯 数组)
  8. keep-alive 是什么?
  9. 3蛋白wb_干货分享:三个蛋白组学数据库的应用
  10. java读取apk、ipa包名、版本名、版本号等信息
  11. qt_cannot find -lxxx, 链接动态/静态库失败
  12. 李刚疯狂java讲义第3_【Java】-NO.16.EBook.4.Java.1.009-【疯狂Java讲义第3版 李刚】- 泛型...
  13. PaddleOCR数字仪表识别——1.字体背景删选
  14. Axure RP9 中继器的简单操作(1)
  15. 【uni-app】H5的返回拦截经验分享
  16. UE4 使用CustomMesh动态创建网格体
  17. XXU邮箱,和客户端同步 pku
  18. 电路分析题目详解(三)
  19. 配电网重构matlab程序,配电网络重构matlab代码
  20. java面试不会怎么办_【必须录用】面试遇到不会回答的问题,该怎么办?

热门文章

  1. ASP.net 母版页 外观设置/样式表设置
  2. JSBridge通信原理
  3. java.lang.IllegalArgumentException: Failed to find configured root that contains .....
  4. 一文读懂 4 线 SPI
  5. 拳王虚拟项目公社:你不可不知的,操作虚拟资源的实用法门
  6. 制作一个图片条幅跑马灯
  7. STM32CubeMX系列10——TFT-LCD的使用(FSMC接口、8080通信协议)
  8. 哈哥的博客阅读指南,一文对接全链路导引 --- 未完待续~
  9. SQL常用查询案例(转)
  10. centos7搭建公共/私有xss平台