android dex 分包处理利弊

来源:互联网 发布:星际公民 知乎 编辑:程序博客网 时间:2024/05/17 06:25
分包产生的原因:
  早期的 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 <output file>] <application path>
1)调用 proguard 的 shrink 操作来生成一个临时 jar 包; 
2)将生成的临时 jar 包和输入的文件集合作为参数,然后调用com.android.multidex.MainDexListBuilder 来生成主 dex 文件列表。在 shrink 这一步,proguard 会根据 mainDexClasses.rules 规则保留需要的类和类成员,并丢弃不需要的类和类成员。也就是说,上面 shrink 步骤生成的临时 jar 包里面保留了符合 mainDexClasses.rules 规则的类,这些类是需要放在主 dex 中的入口类。


结论:
当前工程对于dex的分包策略遵循google默认的方式,开始认为需要手动分包是在冷启动耗时太久的情况下,在进行打包之后,没有了instant.zip热部署的压缩包之后,程序的冷启动时间已经是可控的,如果认为时间太久的话,可以将application中的一些预加载内容放到其他线程中进行。就可以加快冷启动速度。
况且开发者干预dex加载策略的时候会造成在每次的应用添加新模块与功能时候,都需要进行维护main-dex-list.txt就是主dex分包列表,如果缺一个就会产生错误,造成很大的困扰,不建议去干预google的dex分包策略。
0 0