我们在开发项目时,喜欢引入好多的第三方包,大大的方便了我们的开发,但同时,因为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库的更多相关文章

  1. 通过源码看android系列之AsyncTask

    整天用AsyncTask,但它的内部原理一直没有特意去研究,今天趁着有时间,码下它的原理. 具体用法就不再说明,相信大家已经用得很熟练了,我们今天就从它怎么运行开始说.先新建好我们的AsyncTask ...

  2. 通过源码看原理之 selenium

    # selenium的历史1. selenium1.x:这个时候的selenium,使用的是JavaScript注入技术与浏览器打交道,需要Selenium RC启动一个Server,将操作Web元素 ...

  3. Kafka详解六:Kafka如何通过源码实现监控

    问题导读: 1.kafka的消费者组的消费偏移存储,kafka支持两个版本?        2.ConsumerOffsetChecker类的作用是什么?        3.Kafka如何通过源码实现 ...

  4. 通过源码安装PostgresSQL

    通过源码安装PostgresSQL 1.1 下载源码包环境: Centos6.8 64位 yum -y install bison flex readline-devel zlib-devel yum ...

  5. 通过源码了解ASP.NET MVC 几种Filter的执行过程

    一.前言 之前也阅读过MVC的源码,并了解过各个模块的运行原理和执行过程,但都没有形成文章(所以也忘得特别快),总感觉分析源码是大神的工作,而且很多人觉得平时根本不需要知道这些,会用就行了.其实阅读源 ...

  6. 追源索骥:透过源码看懂Flink核心框架的执行流程

    li,ol.inline>li{display:inline-block;padding-right:5px;padding-left:5px}dl{margin-bottom:20px}dt, ...

  7. 通过源码了解ASP.NET MVC 几种Filter的执行过程 在Winform中菜单动态添加“最近使用文件”

    通过源码了解ASP.NET MVC 几种Filter的执行过程   一.前言 之前也阅读过MVC的源码,并了解过各个模块的运行原理和执行过程,但都没有形成文章(所以也忘得特别快),总感觉分析源码是大神 ...

  8. Linux下通过源码编译安装程序

    本文简单的记录了下,在linux下如何通过源码安装程序,以及相关的知识.(大神勿喷^_^) 一.程序的组成部分 Linux下程序大都是由以下几部分组成: 二进制文件:也就是可以运行的程序文件 库文件: ...

  9. 在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 从 ...

随机推荐

  1. Quartz1.8.5例子(十一)

    /* * Copyright 2005 - 2009 Terracotta, Inc. * * Licensed under the Apache License, Version 2.0 (the ...

  2. TCP/IP入门学习(1)---分层概述

    本文旨在记述一些学习中的笔记 OSI分层:应用层,表示层,会话层,传输层,网络层,数据链路层,物理层 详细点: 1.应用层:为应用程序提供服务并规定程序中通信相关细节. 2.表示层:将应用处理的信息转 ...

  3. python和django的一些小技巧(locals()...)

    locals() 技巧: 思考一下我们对 current_datetime 的最后一次赋值: >>> import datetime >>> def current ...

  4. Mysql table ful

    http://ourmysql.com/archives/1327 http://blog.csdn.net/kevon_sun/article/details/7967728 http://my.o ...

  5. VLAN间单臂路由访问

    实验书上的拓朴图: 注意TRUNK端口和路由器子端口设置,可以承载不同的VLAN标签. 交换机(用2691加交换模块实现的): Building configuration... Current co ...

  6. Android之读取 AndroidManifest.xml 中的数据

    转:http://www.2cto.com/kf/201208/151123.html 下来示例如何读取这些数据. 1 版本信息.应用名称 2 Appliction 的Meta-data 3 Acti ...

  7. linux ERROR: ld.so: object '/lib/libcwait.so' from /etc/ld.so.preload cannot be preloaded: ignored.

    [root@ora9i 3238244]# lsb_release -a LSB Version:    :core-3.0-amd64:core-3.0-ia32:core-3.0-noarch:g ...

  8. bzoj1559

    自动机上状压dp,把单词是否存在压成二进制位注意这里面某些单词会包含其他单词,所以某些自动机上有些状态点对应多个二进制位方案只要再顺着有方案的状态搜一遍即可 ..,'a'..'z'] of longi ...

  9. ECSHOP首页调用指定分类下的商品

    转:http://bbs.ecshop.com/thread-1123207-1-1.html 调用某个分类下的商品,方法有很多种的,不过都需要先在后台设置模板那里设置显示和显示条数, 然后在需要调用 ...

  10. Devexpress之DateEdit学习,可选择日期时 zt

    DateEdit默认是是值只可以选择日期的,下面就来看看怎么设置可以选择时间. 代码如下. 设置以下属性 dateEdit1.Properties.VistaDisplayMode = DevExpr ...