分包产生的原因:
早期的 Android 系统中,DexOpt 有两个问题。(单个 dex 文件方法总数65K 的限制,Dexopt 的 LinearAlloc[应用的方法信息存储] 限制)
(一):DexOpt 会把每一个类的方法 id 检索起来,存在一个链表结构里面,但是这个链表的长度是用一个 short 类型来保存的,导致了方法 id 的数目不能够超过65536个。当一个项目足够大的时候,显然这个方法数的上限是不够的。
(二):Dexopt 使用 LinearAlloc 来存储应用的方法信息。Dalvik LinearAlloc 是一个固定大小的缓冲区。在Android 版本的历史上,LinearAlloc 分别经历了4M/5M/8M/16M限制。Android 2.2和2.3的缓冲区只有5MB,Android 4.x提高到了8MB 或16MB。当方法数量过多导致超出缓冲区大小时,也会造成dexopt崩溃。
1.当前的app分包策略:
a.debug包
b.打线上包之后的dex分包大小 (在debug的模式下的时候,为了能够进行热部署,很多的dex文件都被压缩到了instant-run.zip包中,所以classes.dex与classes2.dex的包很小,打包时候会反应出dex分包的结果。)
2.app启动耗时有与冷启动
从dex 的大小也直接影响启动速度,即从dex 越小则启动越快,所以在classes.dex中的方法越少大小越小,启动就越快。
首次启动时Dalvik虚拟机会对classes.dex执行dexopt操作,生成ODEX文件,这个过程非常耗时,而执行MultiDex.install()必然会再次对classes2.dex执行dexopt等操作,所有这些操作必须在5秒内完成,否则就ANR;
3.工程启动时间统计脚本:
adb shell am start -W
***/***.base.LauncherActivity、
(1)工程从准备加载到launcheractivity到CardDeskActivity启动一共花费时间:
a.从debug包里面进行的耗时统计:
C:\Users\Administrator>adb shell am start -W ***/***.activity.base.LauncherActivity
Starting: Intent { act=android.intent.action.MAIN cat=[android.intent.category.L
AUNCHER] cmp=***/.activity.base.LauncherActivity }
Status: ok
Activity: ***/.activity.carddesk.CardDeskActivity
ThisTime: 1717 //一连串的activity 启动中,最后一个activity的启动的时间
TotalTime: 5114 //启动一连串的acitivity中第一个activity启动的时间
WaitTime: 5205 //startActivityAndWait()调用耗时,也就是用户能够感觉到的时间
Complete //冷启动的启动时间已经到了5秒,耗时已经很高了。
b.从打包里面进行的耗时统计:
冷启动时候:
C:\Users\Administrator>adb shell am start -W /com.iscs.mobilew
cs.activity.base.LauncherActivity
Starting: Intent { act=android.intent.action.MAIN cat=[android.intent.category.L
AUNCHER] cmp=/.activity.base.LauncherActivity }
Status: ok
Activity: /.activity.login.LoginActivity
ThisTime: 280
TotalTime: 1719
WaitTime: 1771
Complete
C:\Users\Administrator\Desktop\分包>adb shell am start -W /com
.iscs.mobilewcs.activity.base.LauncherActivity
Starting: Intent { act=android.intent.action.MAIN cat=[android.intent.category.L
AUNCHER] cmp=/.activity.base.LauncherActivity }
Status: ok
Activity: /.activity.login.LoginActivity
ThisTime: 454
TotalTime: 2487
WaitTime: 2532
已经打开过得app情况:
C:\Users\Administrator>adb shell am start -W /com.iscs.mobilew
cs.activity.base.LauncherActivity
Starting: Intent { act=android.intent.action.MAIN cat=[android.intent.category.L
AUNCHER] cmp=/.activity.base.LauncherActivity }
Status: ok
Activity: /.activity.login.LoginActivity
ThisTime: 125
TotalTime: 288
WaitTime: 319
Complete
(2).Apk的打包策略,从中进行干预apk的生成与dex的分包策略:
Application.attachBaseContext是我们能控制的最早执行的代码,在这个方法里面执行MultiDex.install()无疑是最佳时机。
为了实现产生多个DEX包,我们可以在生成DEX文件的这一步中, 在Ant或gradle中自定义一个Task来干预DEX产生的过程,从而产生多个DEX.
没有自己进行干预包的加载策略时候,google的默认的加载顺序是:install-->installsecoadarydex-->v19/v14/v4-->expandFieldarray-->生成dexpathlist;
MultiDex自动拆包带来的问题:
在冷启动时因为需要安装DEX文件,如果DEX文件过大时,处理时间过长,很容易引发ANR(Application Not Responding);
采用MultiDex方案的应用可能不能在低于Android 4.0 (API level 14) 机器上启动,这个主要是因为Dalvik linearAlloc的一个bug ;
采用MultiDex方案的应用因为需要申请一个很大的内存,在运行时可能导致程序的崩溃,这个主要是因为Dalvik linearAlloc 的一个限制,这个限制在 Android 4.0 (API level 14)已经增加了, 应用也有可能在低于 Android 5.0 (API level 21)版本的机器上触发这个限制
拆包的maindexlist.txt列表中,应该先行进行加载的类列表生成策略:
(1)网上能够找得到的方法比较少,美团有自己的脚本程序找启动依赖类,但人家没开源还好Google到了CDA(Class Dependency Analyzer),通过这个工具,基本能找到启动过程中所有Activity、Application等相关依赖类,通常会有一定偏差(会将某些系统方法也找出来了)。
这时还需结合App的所有类来作进一步优化(获取App所有类只需反编译dex文件形成jar,解压jar包,再用shell相关工具处理即可得到),取两者的交集基本就能找出所有启动依赖类了。这里有一点需注意:必须以debug版本的App来分析.(release包有混淆相关的操作。)
(2)Android SDK 从 build tools 21 开始提供了 mainDexClasses 脚本来生成主 dex 的文件列表。
mainDexClasses [--output