前言

其实大家都知道的Android是使用Java作为开发语言,但是他使用的虚拟机却并不是传统的JVM,在4.4以前Android使用Dalvik虚拟机,之后使用ART(Android Runtime).

Dalvik和ART与传统的JVM不同的地方在于,为了更加高效的在移动终端运行,Google重新定义了一套Dalvik字节码,用于在Dalvik和ART虚拟机上运行.

因此如果你希望自己在本地生成的Java代码能够在Android机器上运行,就必须通过转码的形式生成一份Dex文件.

生成Dex文件

当我们完成Java代码的书写后,需要使用dx命令将Java代码由javac生成的class文件编译为Android虚拟机可识别的dex文件.

编译命令如下

1 dx --dex --output 'dex' 'target'

其中dex代表将要输出的dex文件名,target代表用于作为输入的文件,可以为class或jar文件.

解压生成的dex文件后,你会发现一个叫做classes.dex的文件,是不是很眼熟? 在每个Android的APK中都会存在这么一个文件,他才是所有代码的集合,类似于class文件.

生成完了Dex文件,接下来,让我们看看如何在Android设备上运行它.

加载Dex文件

下面是一个Dex文件加载的Demo:

 1 Context context = getBaseContext();
 2 DexClassLoader loader = new DexClassLoader("/sdcard/demo.jar",
 3     context.getCacheDir().getAbsolutePath(), null, context.getClassLoader());
 4 try { 5     Class<?> clazz = loader.loadClass("com.kifile.sample.Sample");
 6     Object object = clazz.newInstance();
 7     Method method = clazz.getDeclaredMethod("println");
 8     if (method != null) { 9         method.invoke(object);
10     }
11 } catch (ClassNotFoundException e) {12     e.printStackTrace();
13 } catch (InstantiationException e) {14     e.printStackTrace();
15 } catch (IllegalAccessException e) {16     e.printStackTrace();
17 } catch (NoSuchMethodException e) {18     e.printStackTrace();
19 } catch (InvocationTargetException e) {20     e.printStackTrace();
21 }

通过以上代码,能够从位于/sdcard/demo.jar的文件中读取出一个”com.kifile.sample.Sample”类,并生成实例,动态调用其内部的”println”方法.

类加载流程分析

说了这么多了,都是教大家怎么在Android下动态加载java文件,现在我们来深入了解一下Android的类加载流程.

传统Java类加载流程

首先对于我们先简单说一下传统的Java程序的类加载流程.

几乎每一个Java类进行加载的时候都是通过ClassLoader进行加载的,当涉及引用新的类时,系统会自动调用当前类的ClassLoader区加载新类.

在加载的时候,首先判断类有无加载,然后再从父ClassLoader中尝试加载Class(之所以优先从父ClassLoader加载,是为了方便其他的兄弟ClassLoader能够复用这个Class),当父ClassLoader也无法找到时,才通过findClass方法进行自查找.

Dex类加载流程

熟悉了这个之后,我们再来看看Android的类加载流程.

其实两者的基本流程都是类似的:在能够复用类的时候,尽量复用类,实在找不到Class,才自己去查找,但Android程序毕竟同普通的Java程序有所不同,

首先Android程序里所有组件的启动均在ActivityThread中执行,同时在ActivityThread中会拥有一个LoadedApk的对象,在LoadedApk对象中,保存了包相关的数据,例如Dex文件和资源文件存放地址.

当我们启动Application,Activity,Service等组件的时候,系统从LoadedApk中获取ClassLoader对象,然后通过loadClass的方式,加载类.

这个ClassLoader对象就是PathClassLoader,因此对于Android应用而言,他几乎所有的类都是通过PathClassLoader进行加载(除了诸如String,System等类,他们在Dalvik虚拟机创建时就注册了).

所以我们需要关心的就是PathClassLoader的加载流程,PathClassLoader与上面样例代码中的DexClassLoader一样继承自BaseDexClassLoader.

我们看看BaseDexClassLoader类加载器的加载流程图.

以上文件的源码路径分别为:

$ANDROID/libcore/dalvik/src/main/java/dalvik/system/BaseDexClassLoader.java $ANDROID/libcore/dalvik/src/main/java/dalvik/system/DexPathList.java $ANDROID/libcore/dalvik/src/main/java/dalvik/system/DexFile.java $ANDROID/art/runtime/native/dalvik_system_DexFile.cc $ANDROID/art/runtime/native/java_lang_VMClassLoader.cc

如上所示,当DexClassLoader需要加载类时,先通过VMClassLoader查询该类是否已经加载,如果尚未加载就通过创建时传入的dexPath地址获取到dex包路径,然后根据Dex包路径去实例化Class对象,并存入虚拟机中,然后返回.

当你成功的从ClassLoader获取到Class时,其实在dalvik_system_DexFile中已经将当前类放置到ClassLoader对应的一张Hash表中,便于你下次重新获取:

1 #$ANDROID/art/runtime/native/dalvik_system_DexFile.cc#DexFile_defineClassNative
2 class_linker->InsertDexFileInToClassLoader(soa.Decode<mirror::Object*>(dexFile),
3     class_loader.Get());
1 #$ANDROID/art/runtime/class_linker.cc#InsertDexFileInToClassLoader
2 class_table->InsertWithHash(klass, hash);

当我们通过ClassLoader获取类时,通过以下代码,将ClassLoader本身传入本地代码块,从ClassLoader对应的Hash表中查询类是否已经定义.

1 protected final Class<?> findLoadedClass(String className) {2     ClassLoader loader;
3     if (this == BootClassLoader.getInstance())
4         loader = null;
5     else
6         loader = this;
7     return VMClassLoader.findLoadedClass(loader, className);
8 }

以此,Android就完成了他的类加载流程.

原文地址: http://blog.kifile.com/android/2015/11/10/dex_class_loader.html

Android平台类加载流程源码分析相关推荐

  1. Android电话拨打流程源码分析

    前面分析了电话拨号界面及电话呼叫界面,由于Android的电话Phone设计的很复杂,因此先从UI层入手分析.想要了解Android的电话拨号UI,请查看Android电话拨号UI分析,电话拨号UI在 ...

  2. Android 电话博大流程源码分析

    前面分析了电话拨号界面及电话呼叫界面,由于Android的电话Phone设计的很复杂,因此先从UI层入手分析.想要了解Android的电话拨号UI,请查看Android电话拨号UI分析,电话拨号UI在 ...

  3. Activity启动流程源码分析(基于Android N)

    Activity启动流程源码分析 一个Activity启动分为两种启动方式,一种是从Launcher界面上的图标点击启动,另一种是从一个Activity中设置按钮点击启动另外一个Activity.这里 ...

  4. Activity启动流程源码分析-浅析生命周期函数

    源码分析 接着上一篇 Activity启动流程源码分析-setContentView源码阅读 的讲解,本节介绍一下Activity的生命周期函数何时被调用 要看Activity的生命周期函数何时被调用 ...

  5. OkHttp原理流程源码分析

    OkHttp已经是非常流行的android客户端的网络请求框架,我其实在项目中使用也已经好几年了,之前一直把重心放在如何快速的搞定业务上.迭代的效率上,这一点来讲,对于一个公司优秀员工是没有毛病的.但 ...

  6. 【源码分析】storm拓扑运行全流程源码分析

    [源码分析]storm拓扑运行全流程源码分析 @(STORM)[storm] 源码分析storm拓扑运行全流程源码分析 一拓扑提交流程 一stormpy 1storm jar 2def jar 3ex ...

  7. nimble源码学习——广播流程源码分析1

    广播流程源码分析1 在controller层有多种状态:广播.空闲.连接等等,这次分析的是广播这个状态或者叫 做角色.在前面controller层循环的分析中,可以明确controller层也有eve ...

  8. YOLOv3反向传播原理 之 全流程源码分析

    YOLOv3反向传播原理 之 全流程源码分析 1.YOLOv3网络训练中反向传播主体流程 1.1 初始化 1.2 batch内梯度累加 1.3 network和layer 中的关键变量 2.YOLO层 ...

  9. SpringBoot2 | SpringBoot启动流程源码分析(一)

    首页 博客 专栏·视频 下载 论坛 问答 代码 直播 能力认证 高校 会员中心 收藏 动态 消息 创作中心 SpringBoot2 | SpringBoot启动流程源码分析(一) 置顶 张书康 201 ...

最新文章

  1. 房间计费系统改造——数据库设计
  2. oracle dbms overflow,Oracle DBA课程系列笔记(12_1)
  3. 商务英语老师给的6个建议
  4. 浅析 Linux 初始化 init 系统,第 1 部分: sysvinit
  5. python 获取浏览器句柄下的网页控件,Python获取浏览器窗口句柄过程解析
  6. 《疯狂Java讲义》(十五)---- 内部类
  7. 虚拟机VMware的Ubuntu下安装tensorflow详解
  8. 三边定位算法 matlab,三边测量法的MATLAB定位程序说明.doc
  9. linux文件增加自定义属性,Linux 笔记...文件和目录属性useradd、userdel、usermod 、passwd...
  10. choose标签使用
  11. 论文:Real-Time Referring Expression Comprehension by Single-Stage Grounding Network
  12. PNG或JPEG或PDF转eps
  13. Linux目录文件操作命令篇--(工作常用命令的深度使用,小白必收藏)
  14. 短波红外成像技术与原理
  15. echarts地图城市坐标
  16. 《匆匆那年》的你,还记得吗?数学中的那些有(hui)趣(se)的定理(5)——鸡爪定理
  17. python Pygame的具体使用讲解
  18. TypeScript (TS
  19. linux awk sed strace,docker 中进行strace的三种方式
  20. [BZOJ2548][Ctsc2002]灭鼠行动(大模拟)

热门文章

  1. Oracle delete truncate drop 的区别
  2. mysql 的自动启动 使用配置文件 /etc/my.cnf
  3. Ubuntu16安装anaconda没有这个文件或者目录
  4. 浅析Python中的序列化存储的方法
  5. Python学习笔记:线程和进程(合),分布式进程
  6. Makefile文件生成
  7. Linux系统中查看图片信息
  8. 网站运营之做到SEO操作视频教程【21讲】
  9. 12 月机器学习新书:《可解释机器学习方法的局限》,免费下载!
  10. 试利用记录型信号量和pv操作写出_计算机操作系统知识点汇总