通过源码看android系列之multidex库
我们在开发项目时,喜欢引入好多的第三方包,大大的方便了我们的开发,但同时,因为android方法总数的限制,不能超过65k,然而呢,随着我们的开发,65k最终还是会超过,所以,google就给出了这个解决方案,但一直好奇它是内部是怎么实现的,我们今天就来根据源码来看看这个包到底做了什么,怎么把多个dex读取出来的。先看下这个包里面都有哪些类:

我们首先看MultiDexApplication,只要我们我们自己的Application继承MultiDexApplication就可以解决问题,那我们就来看看,它里面做了什么,只有一个方法,重写了attachBaseContext()。
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
MultiDex.install(this);
}
我们看到,具体的实现是直接调用的Multidex,随后我们来看下install()里面有些什么(关键代码):
public static void install(Context context) {
try {
.
.
.
/* The patched class loader is expected to be a descendant of
* dalvik.system.BaseDexClassLoader. We modify its
* dalvik.system.DexPathList pathList field to append additional DEX
* file entries.
*/
ClassLoader loader;
try {
loader = context.getClassLoader();
} catch (RuntimeException e) {
}
File dexDir = new File(applicationInfo.dataDir, SECONDARY_FOLDER_NAME);
List<File> files = MultiDexExtractor.load(context, applicationInfo, dexDir, false);
if (checkValidZipFiles(files)) {
installSecondaryDexes(loader, dexDir, files);
} else {
.
.
.
}
}
} catch (Exception e) {
}
}
通过ClassLoader的注释我们已经知道,主dex文件的路径被存储在BaseDexClassLoader中的pathList,这样就清楚了,下面所要做的就是把其它的dex文件路径也找出来,添加到pathList上面即可。这里面的loader就是BaseDexClassLoader的实例
行18 MultiDexExtractor这个类,从字面上即可知道它是提取dex信息的,load方法即会根据dex命名规则从指定路径下提取其它dex文件(不包含主dex),具体实现可自行看实现,这样,所有的次dex文件都被提取出来,赋值给了files
行19 会检查所有以上文件是否是有效的zip文件,如果有一个false,就会重新提取dex文件
行20 检查没有问题后,就会执行installSecondaryDexes(),已经很明显知道接下来要做什么了,我们来看这个方法里面都做了什么。
private static void installSecondaryDexes(ClassLoader loader, File dexDir, List<File> files)
throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException,
InvocationTargetException, NoSuchMethodException, IOException {
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, dexDir);
} else {
V4.install(loader, files);
}
}
}
代码写的很明显,我们来单看V19的实现
private static void install(ClassLoader loader, List<File> additionalClassPathEntries,
File optimizedDirectory)
throws IllegalArgumentException, IllegalAccessException,
NoSuchFieldException, InvocationTargetException, NoSuchMethodException {
/* The patched class loader is expected to be a descendant of
* dalvik.system.BaseDexClassLoader. We modify its
* dalvik.system.DexPathList pathList field to append additional DEX
* file entries.
*/
Field pathListField = findField(loader, "pathList");
Object dexPathList = pathListField.get(loader);
ArrayList<IOException> suppressedExceptions = new ArrayList<IOException>();
expandFieldArray(dexPathList, "dexElements", makeDexElements(dexPathList,
new ArrayList<File>(additionalClassPathEntries), optimizedDirectory,
suppressedExceptions));
.
.
.
}
我们前面说的注释又出现了,看来,具体的实现操作就在这里了,主要是用反射来修改它的值,我们主要来看几个主要的操作方法:
行13 这里面有两个方法需要注意makeDexElements()和expandFieldArray(),先来说第一个,我们知道在实例化BaseDexClassLoader时,会把主dex的路径信息存放到pathList里面,而DexPathList内部其它是把dex的路径存储在了一个Element数组中,所以,看makeDexElements()就知道,这个方法,会把传入的dex文件通过反射组装成适合DexPathList内部用的Element数组。
private static Object[] makeDexElements(
Object dexPathList, ArrayList<File> files, File optimizedDirectory,
ArrayList<IOException> suppressedExceptions)
throws IllegalAccessException, InvocationTargetException,
NoSuchMethodException {
Method makeDexElements =
findMethod(dexPathList, "makeDexElements", ArrayList.class, File.class,
ArrayList.class); return (Object[]) makeDexElements.invoke(dexPathList, files, optimizedDirectory,
suppressedExceptions);
}
看它代码是反射调用DexPathList里面的makeDexElements(),但我一直没有找到这个方法,不知道是代码版本的问题还是其它原因,望知道的大神告诉一下。接下来我们再看expandFieldArray()
private static void expandFieldArray(Object instance, String fieldName,
Object[] extraElements) throws NoSuchFieldException, IllegalArgumentException,
IllegalAccessException {
Field jlrField = findField(instance, fieldName);
Object[] original = (Object[]) jlrField.get(instance);
Object[] combined = (Object[]) Array.newInstance(
original.getClass().getComponentType(), original.length + extraElements.length);
System.arraycopy(original, 0, combined, 0, original.length);
System.arraycopy(extraElements, 0, combined, original.length, extraElements.length);
jlrField.set(instance, combined);
}
可看出,又重新建立了一个Element[],把原来和新的数据都放上去,最后赋给dexElements。这样就可以把所有的dex都读取出来了。
通过源码看android系列之multidex库的更多相关文章
- 通过源码看android系列之AsyncTask
整天用AsyncTask,但它的内部原理一直没有特意去研究,今天趁着有时间,码下它的原理. 具体用法就不再说明,相信大家已经用得很熟练了,我们今天就从它怎么运行开始说.先新建好我们的AsyncTask ...
- 通过源码看原理之 selenium
# selenium的历史1. selenium1.x:这个时候的selenium,使用的是JavaScript注入技术与浏览器打交道,需要Selenium RC启动一个Server,将操作Web元素 ...
- Kafka详解六:Kafka如何通过源码实现监控
问题导读: 1.kafka的消费者组的消费偏移存储,kafka支持两个版本? 2.ConsumerOffsetChecker类的作用是什么? 3.Kafka如何通过源码实现 ...
- 通过源码安装PostgresSQL
通过源码安装PostgresSQL 1.1 下载源码包环境: Centos6.8 64位 yum -y install bison flex readline-devel zlib-devel yum ...
- 通过源码了解ASP.NET MVC 几种Filter的执行过程
一.前言 之前也阅读过MVC的源码,并了解过各个模块的运行原理和执行过程,但都没有形成文章(所以也忘得特别快),总感觉分析源码是大神的工作,而且很多人觉得平时根本不需要知道这些,会用就行了.其实阅读源 ...
- 追源索骥:透过源码看懂Flink核心框架的执行流程
li,ol.inline>li{display:inline-block;padding-right:5px;padding-left:5px}dl{margin-bottom:20px}dt, ...
- 通过源码了解ASP.NET MVC 几种Filter的执行过程 在Winform中菜单动态添加“最近使用文件”
通过源码了解ASP.NET MVC 几种Filter的执行过程 一.前言 之前也阅读过MVC的源码,并了解过各个模块的运行原理和执行过程,但都没有形成文章(所以也忘得特别快),总感觉分析源码是大神 ...
- Linux下通过源码编译安装程序
本文简单的记录了下,在linux下如何通过源码安装程序,以及相关的知识.(大神勿喷^_^) 一.程序的组成部分 Linux下程序大都是由以下几部分组成: 二进制文件:也就是可以运行的程序文件 库文件: ...
- 在centos6.7通过源码安装python3.6.7报错“zipimport.ZipImportError: can't decompress data; zlib not available”
在centos6.7通过源码安装python3.6.7报错: zipimport.ZipImportError: can't decompress data; zlib not available 从 ...
随机推荐
- XCode 项目配置说明
初学XCode最让人头疼的就是项目各属性设置,各种不解,这里做个总结: 项目配置: 基本项(Basic) 1.Architectures(指令集)——设置你想支持的指令集,目前ios的指令集有以下几种 ...
- 当今app行业 比较流行的 简称 汇总
B=Business,即企业:C=Customers,即消费者,这样就好理解这些简称了:M=Medium,即媒介B2B:(Business To Business)商家对商家进行交易 如:阿里巴巴.生 ...
- redis入门教程
21) Redis 简介Redis 是一个开源的使用 ANSI C 语言编写.支持网络.可基于内存亦可持久化的日志型.Key-Value 数据库.2) 数据类型2.1. Redis 的 KeyRedi ...
- 3.2.3 使用Parameters动态生成where语句_JasperReports iRepor
$P!{ParametersName}格式的SQL语句 来源:http://book.2cto.com/201306/24311.html#12732-hi-1-66926-020ef5b321fac ...
- Celery Flower监控,完美搞定
XXXX啊,,从上午就看到QUEQUE有问题,但一直不晓得哪里出了问题, 后来,安装上FLOWER看一下,队列就出来了... 神器啊.. 安装不说,运行很EASY.. celery flower -A ...
- 关于v$sql_bind_capture 的问题
---先清空shared_pool SQL> alter system flush shared_pool; System altered. SQL> col value_STRING f ...
- 【CF】135 Div2 Choosing Capital for Treeland
树形结构,挺有意思的题目.不难. /* 219D */ #include <iostream> #include <string> #include <map> # ...
- IIS 7.0, ASP.NET, pipelines, modules, handlers, and preconditions
1.0 What is the IIS Pipeline Conceptually, the IIS pipeline is a state machine with the following st ...
- Poj 3683-Priest John's Busiest Day 2-sat,拓扑排序
Priest John's Busiest Day Time Limit: 2000MS Memory Limit: 65536K Total Submissions: 8872 Accept ...
- javascript检测属性
javascript 对象可以看做属性的集合.我们可以通过三种方式来判断某个属性是否存在于某个对象中. 1.in操作符 var o={x:1} "x" in o //True :x ...