Android 9.0 ART编译分析(三)-虚拟机触发dex2oat编译流程

原创内容,转载请注明出处,多谢配合。

这部分是针对动态加载插件的编译,主要是由ClassLoader来触发的,下面简单总结下。

一、Android 的ClassLoder介绍
1.1 ClassLoder类

ClassLoader 抽象父类 。
BootClassLoader 加载Android 系统编译文件。
BaseDexClassLoader PathClassLoader和DexClassLoader的父类,主要功能执行者。
PathClassLoader 加载已被安装的应用路径中.dex 文件。
DexClassLoader 加载指定路径中的.dex 文件。

1.2 初始化

getClassloader实际上就是选择对应classLoader并保证初始化的过程,常用的是Context去获取的。

/frameworks/base/core/java/android/app/ContextImpl.java

@Override
public ClassLoader getClassLoader() {
 return mClassLoader != null ? mClassLoader : (mPackageInfo != null ? mPackageInfo.getClassLoader() : ClassLoader.getSystemClassLoader());
}

不管是LoadedApk. getClassLoader 还是ClassLoader.getSystemClassLoader(),针对系统级别的类加载都是PathClassLoader。

1.3 DexClassLoader与PathClassLoader构造方法说明

DexClassLoader与PathClassLoader均继承BaseDexClassLoader,内部只包含构造方法,真正的功能实现都在BaseDexClassLoader。

两者构造方法解析:

public DexClassLoader(String dexPath, String optimizedDirectory, String librarySearchPath, ClassLoader parent) { / /热修复使用这个
    super((String)null, (File)null, (String)null, (ClassLoader)null); 
   throw new RuntimeException("Stub!"); 
}
参数:
dexPath:dex 文件路径列表,多个路径使用”:”分隔
optimizedDirectory:经过优化的 dex 文件(odex)文件输出目录
librarySearchPath:动态库路径(将被添加到 app 动态库搜索路径列表中)
parent:这是一个 ClassLoader,这个参数的主要作用是保留 java 中 ClassLoader 的委托机制。
public PathClassLoader(String dexPath, String librarySearchPath, ClassLoader parent) { 
    super((String)null, (File)null, (String)null, (ClassLoader)null); 
   throw new RuntimeException("Stub!"); 
}
参数:
dexPath:文件或者目录的列表
librarySearchPath:包含 lib 库的目录列表
parent:父类加载器

二、ClassLoader相关流程

先上流程图


Android 9.0 ART编译分析(三)-虚拟机触发dex2oat编译流程_第1张图片
动态加载插件直接触发dex2oat编译流程

注:这里loadClass线路点到为止,因为它并不是本篇文章的重点。

两条路线解析:
先走dex编译,再走loadClass类加载。

1)6-17是makeDexElements 保存dexElements 并触发dex编译过程

DexPathList有一个Element[] dexElements; 是用来保存dex/resource(class path) 的集合,由makeDexElements来收集。makeDexElements 方法会遍历所有dex path,并将它们一个个封装为Element,保存到Element[]中,长度不够通过copyOf翻倍扩容。另外会通过loadDexFile开始走编译路线。
编译的核心方法在oat_file_manager.cc中的OpenDexFilesFromOat,这里通过oat_file_assistant.cc 执行isUpToDate判断dex文件是否需要编译,如果需要走它的MakeUpToDate方法,执行编译。
MakeUpToDate中如果需要执行编译会走GenerateOatFileNoChecks,最终调用其Dex2Oat方法,调整好参数传给dex2oat这个执行文件去Exec。

2)1-5是loadClass类加载过程

在ClassLoader中执行classLoader:这里遵循双亲委派机制,先判断当前ClassLoader是否加载过,如果没有就让父类去加载,父类都不加载,再轮到子类去加载。
BaseDexClassLoader执行findClass,实际是通过DexPathList去执行findClass。
因为之前makeDexElements给Element[] dexElements赋值了,这里DexPathList执行的findClass实际上是遍历所有dexElements,每一个element都执行findClass
而最终是交给DexFile去做的类加载:其具体流程不赘述,跟java ClassLoader差不多:
顺序经历如下流程:

  • Loading:类的信息从文件中获取并加载到JVM内存中。
  • Verifying:检查读入的结构是否符合JVM规范。
  • Preparing:验证完正确,会分配一个结构来存储类信息。
  • Resolving: 把这个类的常量池中的所有符号引用转变为直接引用。
  • Initializing:执行静态初始化程序,把静态变量初始化成指定的值。

最终就是将dex字节码文件loader到内存,按虚拟机内存划分区域去存放对应的数据。

你可能感兴趣的:(Android 9.0 ART编译分析(三)-虚拟机触发dex2oat编译流程)