MultiDex加载流程分析
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
,添加到DexPathList
的elements
字段中。
MultiDex加载流程分析相关推荐
- Android6.0 keyguard锁屏加载流程分析
锁屏界面的加载通常在android中有两种方式触发:android系统开机和screenOff(灭屏)后,再screenOn; 先来看 android系统开机时候的锁屏加载流程: 首先在系统启动过程中 ...
- Launcher3 桌面加载流程分析
Launcher3 桌面加载流程分析 主入口Launcher 首先来看Launcher.java的onCreate方法,里面代码很多,只看主流程部分: @Override protected void ...
- linux驱动加载流程分析
linux驱动加载流程分析 内核是如何加载驱动的,有些是编译到内核里面,有些事编译成ko,让系统自动加载.总的说来,在Linux下可以通过两种方式加载驱动程序:静态加载和动态加载. 静态加载就是把驱动 ...
- Spring初始化加载流程分析
关于Spring框架的介绍,网上有很多非常好的详细的文章,如果在本篇博客中没有了解到自己想要的东西,个人能力有限,只能使用博客记录一下自己目前了解的知识点了! 本篇博客将大致介绍一下Spring框架的 ...
- Android 4.0 ICS SystemUI浅析——StatusBar加载流程分析
前面两篇文章< Android 4.0 ICS SystemUI浅析--SystemUI启动流程>.< Android 4.0 ICS SystemUI浅析--StatusBar结构 ...
- Pinpoint Agent加载流程分析
pinpoint 版本:2.0.3-SNAPSHOT pinpoint利用java agent 特性,提供了一个agent jar包,此jar包会在应用运行之前先运行,agent和应用在同一个进程.p ...
- NioEventLoop加载流程分析
一.我们首先看NioEventLoopGroup创建和初始化过程. EventLoopGroup workEventLoopGroup = new NioEventLoopGroup(new Name ...
- 【CEGUI】资源加载流程
CEGUI资源加载流程 CEGUI版本 0.8.7 主要资源类型 Scheme scheme资源(包括图像集.字体资源.窗口外观信息.类型映射)等.可以通过".scheme"&qu ...
- Tomcat:应用加载原理分析
前情回顾 上一篇文章主要了解了一下Tomcat启动入口,以及初步的分析了Tomcat的启动流程,下面我们将会解密Tomcat应用部署的实际流程. 一.直观对比 虽然前面已经说了那么多关于Tomcat的 ...
最新文章
- java新闻爬虫_java实现简单的爬虫之今日头条
- linux g命令,【Linux】常用命令大全
- linux 启动rsyslog服务_我的服务器怎么老这么慢,难道说是被挖矿了?linux开机启动项自查...
- Vue使用html2Canvas和canvas2Image下载二维码会模糊的问题解决方法
- 一个好用的变换类TransformManager
- 无显示器u盘安装centos_无光驱情况下,U盘启动安装CentOS (实测 笔记)
- 如何使用Movavi Video Editor编辑婚礼影片?
- windows开发——配置pthread.h头文件
- sqlserver 触发器语法
- 使用nssm注册 windows服务
- celeste第二章_蔚蓝山Celeste全成就指南_蔚蓝山Celeste全成就获得方法_游戏堡
- TCP协议中的源地址和目的地址是内网地址还是外网地址的测试
- 赤峰市田家炳中学2021高考成绩查询,2021年常州各高中高考成绩排名及放榜最新消息...
- CURL 发送请求详解
- 熵值法原理、应用及其Python实现
- 一文读懂“什么是Web 1.0,Web 2.0,Web 3.0?”
- 神经系统图 基本结构图,神经系统的组织结构图
- android 内嵌 数据库,安卓开发之嵌入式数据库sqlite的操作方法
- HTML5中polygon坐标,SVG Polygon(多边形)
- 基于51单片机GPS定位系统设LCD12864显示(程序+原理图+PCB+论文)