MultiDex加载流程分析

只介绍主要流程,multiDex源码已经上传到https://github.com/AlexSmille/google-android-support-source-analyze,包含了BaseDexClassloader等一些android源码。

1.判断当前是否需要动态加载类

5.0以上,会自动加载一个apk中所有的classesX.dex,但是低于5.0时需要手动导入。

具体判断是否需要手动导入如下:

 static boolean isVMMultidexCapable(String versionString) {//You can verify which runtime is in use by calling System.getProperty("java.vm.version").// If ART is in use, the property's value is "2.0.0" or higher.boolean isMultidexCapable = false;//2.1.0的时候开始支持默认加载外部classes.dex,根据vm版本号判断if (versionString != null) {Matcher matcher = Pattern.compile("(\\d+)\\.(\\d+)(\\.\\d+)?").matcher(versionString);if (matcher.matches()) {try {int major = Integer.parseInt(matcher.group(1));int minor = Integer.parseInt(matcher.group(2));isMultidexCapable = (major > VM_WITH_MULTIDEX_VERSION_MAJOR)|| ((major == VM_WITH_MULTIDEX_VERSION_MAJOR)&& (minor >= VM_WITH_MULTIDEX_VERSION_MINOR));} catch (NumberFormatException e) {// let isMultidexCapable be false}}}return isMultidexCapable;}

如果已经支持自动导入,则结束流程,否则进入下一步。

2.构造必要参数,调用doInstallation()方法

调用doInstallation()传入四个参数

  • Context mainContext:上下文对象。
  • File sourceApk:安装额apk的存放路径。/data/app/….
  • File dataDir: apk的数据存放目录。/data/data/package/
  • String secondaryFolderName:存放缓存的目录文件名secondary-dexes
  • String prefsKeyPrefix:
  • boolean reinstallOnPatchRecoverableException:如果导入失败,是重试还是直接抛出异常。

3.doInstallation()方法判断一些必要条件

if (installedApk.contains(sourceApk)) {// 是否包含要导入的apk,防止重复导入return;}// 将apk添加到集合中installedApk.add(sourceApk);// 5.0以上不用导入  IS_VM_MULTIDEX_CAPABLEif (Build.VERSION.SDK_INT > MAX_SUPPORTED_SDK_VERSION) {Log.w(TAG, "MultiDex is not guaranteed to work in SDK version "+ Build.VERSION.SDK_INT + ": SDK version higher than "+ MAX_SUPPORTED_SDK_VERSION + " should be backed by "+ "runtime with built-in multidex capabilty but it's not the "+ "case here: java.vm.version=\""+ System.getProperty("java.vm.version") + "\"");}}

4.构造存放classesX.dex的目录/data/data/package/code_cache/secondary-dexes

File dexDir = getDexDir(mainContext, dataDir, secondaryFolderName);

创建dexDir,即存放dex的目录。getDexDir()代码如下:

private static File getDexDir(Context context, File dataDir, String secondaryFolderName)throws IOException {// data/data/package/code_cacheFile cache = new File(dataDir, CODE_CACHE_NAME);try {mkdirChecked(cache);} catch (IOException e) {/* If we can't emulate code_cache, then store to filesDir. This means abandoning useless* files on disk if the device ever updates to android 5+. But since this seems to* happen only on some devices running android 2, this should cause no pollution.*/cache = new File(context.getFilesDir(), CODE_CACHE_NAME);mkdirChecked(cache);}// data/data/package/code_cache/secondary-dexesFile dexDir = new File(cache, secondaryFolderName);mkdirChecked(dexDir);return dexDir;}

5.构造MultiDexExtractor对象,准备提取classesX.dex

// MultiDexExtractor is taking the file lock and keeping it until it is closed.// Keep it open during installSecondaryDexes and through forced extraction to ensure no// extraction or optimizing dexopt is running in parallel.MultiDexExtractor extractor = new MultiDexExtractor(sourceApk, dexDir);

6.通过MultiDexExtractor提取并存储到data/data/package/code_cache/secondary-dexes/目录下

在此步骤中,会将apk中的classesX.dex提取并存放为data/data/package/code_cache/secondary-dexes/package--1.apk.classN.zip,以便后续加载到内存中。

关键方法MultiDexExtractor.load()方法

List<? extends File> load(Context context, String prefsKeyPrefix, boolean forceReload)throws IOException {Log.i(TAG, "MultiDexExtractor.load(" + sourceApk.getPath() + ", " + forceReload + ", " +prefsKeyPrefix + ")");if (!cacheLock.isValid()) {throw new IllegalStateException("MultiDexExtractor was closed");}List<ExtractedDex> files;if (!forceReload && !isModified(context, sourceApk, sourceCrc, prefsKeyPrefix)) {// apk信息没有发生变化try {// 加载已存在的classX.zipfiles = loadExistingExtractions(context, prefsKeyPrefix);} catch (IOException ioe) {Log.w(TAG, "Failed to reload existing extracted secondary dex files,"+ " falling back to fresh extraction", ioe);files = performExtractions();putStoredApkInfo(context, prefsKeyPrefix, getTimeStamp(sourceApk), sourceCrc,files);}} else {if (forceReload) {Log.i(TAG, "Forced extraction must be performed.");} else {Log.i(TAG, "Detected that extraction must be performed.");}// 重新从apk中提取classesX.dexfiles = performExtractions();putStoredApkInfo(context, prefsKeyPrefix, getTimeStamp(sourceApk), sourceCrc,files);}Log.i(TAG, "load found " + files.size() + " secondary dex files");return files;}

根据apk是否有新变化,判断是直接读取之前获取的classX.zip文件,还是从apk中重新提取classes.dex

看一下提取的方法

private List<ExtractedDex> performExtractions() throws IOException {final String extractedFilePrefix = sourceApk.getName() + EXTRACTED_NAME_EXT;// It is safe to fully clear the dex dir because we own the file lock so no other process is// extracting or running optimizing dexopt. It may cause crash of already running// applications if for whatever reason we end up extracting again over a valid extraction.// 清楚缓存目录clearDexDir();List<ExtractedDex> files = new ArrayList<ExtractedDex>();// 获取apk的zip对象final ZipFile apk = new ZipFile(sourceApk);int secondaryNumber = 2;// 从apk中获取对应classesX.dexZipEntry dexFile = apk.getEntry(DEX_PREFIX + secondaryNumber + DEX_SUFFIX);while (dexFile != null) {String fileName = extractedFilePrefix + secondaryNumber + EXTRACTED_SUFFIX;ExtractedDex extractedFile = new ExtractedDex(dexDir, fileName);files.add(extractedFile);Log.i(TAG, "Extraction is needed for file " + extractedFile);int numAttempts = 0;boolean isExtractionSuccessful = false;while (numAttempts < MAX_EXTRACT_ATTEMPTS && !isExtractionSuccessful) {numAttempts++;// Create a zip file (extractedFile) containing only the secondary dex file// (dexFile) from the apk.// 创建zip文件,并提取.dex文件extract(apk, dexFile, extractedFile, extractedFilePrefix);// .... 后面的都是一些判断}}return files;}

此段对应的log如下

// MultidexExtractor.load()
07-04 16:19:52.296 I/MultiDex( 4387): MultiDexExtractor.load(/data/app/com.spearbothy.android-2.apk, false, )
// 有新信息变化,删除老的缓存文件
07-04 16:19:52.306 I/MultiDex( 4387): Detected that extraction must be performed.
07-04 16:19:52.306 I/MultiDex( 4387): Trying to delete old file /data/data/com.spearbothy.android/code_cache/secondary-dexes/com.spearbothy.android-1.apk.classes2.zip of size 185848
07-04 16:19:52.306 I/MultiDex( 4387): Deleted old file /data/data/com.spearbothy.android/code_cache/secondary-dexes/com.spearbothy.android-1.apk.classes2.zip
07-04 16:19:52.306 I/MultiDex( 4387): Trying to delete old file /data/data/com.spearbothy.android/code_cache/secondary-dexes/com.spearbothy.android-1.apk.classes2.dex of size 675216
07-04 16:19:52.306 I/MultiDex( 4387): Deleted old file /data/data/com.spearbothy.android/code_cache/secondary-dexes/com.spearbothy.android-1.apk.classes2.dex
// 提取apk中的class文件
07-04 16:19:52.386 I/MultiDex( 4387): Extraction is needed for file /data/data/com.spearbothy.android/code_cache/secondary-dexes/com.spearbothy.android-2.apk.classes2.zip
07-04 16:19:52.386 I/MultiDex( 4387): Extracting /data/data/com.spearbothy.android/code_cache/secondary-dexes/tmp-com.spearbothy.android-2.apk.classes762107569.zip
07-04 16:19:56.016 I/MultiDex( 4387): Renaming to /data/data/com.spearbothy.android/code_cache/secondary-dexes/com.spearbothy.android-2.apk.classes2.zip
07-04 16:19:56.016 I/MultiDex( 4387): Extraction succeeded '/data/data/com.spearbothy.android/code_cache/secondary-dexes/com.spearbothy.android-2.apk.classes2.zip': length 3843460 - crc: 28802720

7.加载类到BaseDexClassLoader

在第6步的时候,将.dex文件提取出来保存到了对应的目录,那么只需要加载就可以了。

// 获取提出来的`dex`文件目录
files = extractor.load(mainContext, prefsKeyPrefix, true);
// 导入
installSecondaryDexes(loader, dexDir, files);
private static void installSecondaryDexes(ClassLoader loader, File dexDir,List<? extends File> files)throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException,InvocationTargetException, NoSuchMethodException, IOException, SecurityException,ClassNotFoundException, InstantiationException {if (!files.isEmpty()) {if (Build.VERSION.SDK_INT >= 19) {V19.install(loader, files, dexDir);} else if (Build.VERSION.SDK_INT >= 14) {V14.install(loader, files);} else {V4.install(loader, files);}}}

不同版本,加载的方式不同,所以根据sdk版本选择不同的加载方式。因为5.0以上不需要加载,所以只需要到19即可。

虽然加载的方式不同,但核心的就是将每一个.zip转化为DexPathList.Element,添加到DexPathListelements字段中。

MultiDex加载流程分析相关推荐

  1. Android6.0 keyguard锁屏加载流程分析

    锁屏界面的加载通常在android中有两种方式触发:android系统开机和screenOff(灭屏)后,再screenOn; 先来看 android系统开机时候的锁屏加载流程: 首先在系统启动过程中 ...

  2. Launcher3 桌面加载流程分析

    Launcher3 桌面加载流程分析 主入口Launcher 首先来看Launcher.java的onCreate方法,里面代码很多,只看主流程部分: @Override protected void ...

  3. linux驱动加载流程分析

    linux驱动加载流程分析 内核是如何加载驱动的,有些是编译到内核里面,有些事编译成ko,让系统自动加载.总的说来,在Linux下可以通过两种方式加载驱动程序:静态加载和动态加载. 静态加载就是把驱动 ...

  4. Spring初始化加载流程分析

    关于Spring框架的介绍,网上有很多非常好的详细的文章,如果在本篇博客中没有了解到自己想要的东西,个人能力有限,只能使用博客记录一下自己目前了解的知识点了! 本篇博客将大致介绍一下Spring框架的 ...

  5. Android 4.0 ICS SystemUI浅析——StatusBar加载流程分析

    前面两篇文章< Android 4.0 ICS SystemUI浅析--SystemUI启动流程>.< Android 4.0 ICS SystemUI浅析--StatusBar结构 ...

  6. Pinpoint Agent加载流程分析

    pinpoint 版本:2.0.3-SNAPSHOT pinpoint利用java agent 特性,提供了一个agent jar包,此jar包会在应用运行之前先运行,agent和应用在同一个进程.p ...

  7. NioEventLoop加载流程分析

    一.我们首先看NioEventLoopGroup创建和初始化过程. EventLoopGroup workEventLoopGroup = new NioEventLoopGroup(new Name ...

  8. 【CEGUI】资源加载流程

    CEGUI资源加载流程 CEGUI版本 0.8.7 主要资源类型 Scheme scheme资源(包括图像集.字体资源.窗口外观信息.类型映射)等.可以通过".scheme"&qu ...

  9. Tomcat:应用加载原理分析

    前情回顾 上一篇文章主要了解了一下Tomcat启动入口,以及初步的分析了Tomcat的启动流程,下面我们将会解密Tomcat应用部署的实际流程. 一.直观对比 虽然前面已经说了那么多关于Tomcat的 ...

最新文章

  1. java新闻爬虫_java实现简单的爬虫之今日头条
  2. linux g命令,【Linux】常用命令大全
  3. linux 启动rsyslog服务_我的服务器怎么老这么慢,难道说是被挖矿了?linux开机启动项自查...
  4. Vue使用html2Canvas和canvas2Image下载二维码会模糊的问题解决方法
  5. 一个好用的变换类TransformManager
  6. 无显示器u盘安装centos_无光驱情况下,U盘启动安装CentOS (实测 笔记)
  7. 如何使用Movavi Video Editor编辑婚礼影片?
  8. windows开发——配置pthread.h头文件
  9. sqlserver 触发器语法
  10. 使用nssm注册 windows服务
  11. celeste第二章_蔚蓝山Celeste全成就指南_蔚蓝山Celeste全成就获得方法_游戏堡
  12. TCP协议中的源地址和目的地址是内网地址还是外网地址的测试
  13. 赤峰市田家炳中学2021高考成绩查询,2021年常州各高中高考成绩排名及放榜最新消息...
  14. CURL 发送请求详解
  15. 熵值法原理、应用及其Python实现
  16. 一文读懂“什么是Web 1.0,Web 2.0,Web 3.0?”
  17. 神经系统图 基本结构图,神经系统的组织结构图
  18. android 内嵌 数据库,安卓开发之嵌入式数据库sqlite的操作方法
  19. HTML5中polygon坐标,SVG Polygon(多边形)
  20. 基于51单片机GPS定位系统设LCD12864显示(程序+原理图+PCB+论文)

热门文章

  1. MongoDB之聚合查询
  2. 故障艺术中的jpg数据格式使用
  3. wegame显示连接服务器失败,wegame登陆失败提示错误码2怎么办?wegame错误码:2解决方案...
  4. 小猪的Python学习之旅 —— 16.采集拉勾网数据分析Android就业行情
  5. 数据库建模工具PDManer调整建表模板(包含索引)
  6. GoogleEarthView
  7. MYSQL:如何清空表中的数据
  8. 通过Java读取csv文件内容
  9. OSChina 周三乱弹 —— 我们无法成为野兽
  10. U盘安全工具箱 V 1.0 修正版