1.1 Dalvik与ART的区别
Dalvik:在Android5.0之前默认使用的是Dalvik。Dalvik是Google针对与Android平台开发的虚拟机。与JVM运行class文件不同的是Dalvik运行的是dex文件。dex是一种格式,是专为Dalvik设计的一种压缩格式。当dex文件被加载到Dalvik虚拟机当中时,会去解析dex文件得到所有的class然后去解释执行。在Dalvik当中还需要了解的一个概念时JIT(Just In Time)即时编译。由于Dalvik是需要解释执行的,如果全都去解释再执行的话,那么就会消耗很多时间,速度变慢。引入JIT就是为了可以将频繁运行的代码,也就是热点代码,可以在运行的时候编译成机器码。提升运行速度。
ART:ART即Android Run Time,是5.0之后才开始使用。ART与Dalvik不同的是多了一个步骤AOT(ahead of time),也就是预编译,即在应用安装的时候会提前把dex文件编译成OAT文件,将来程序运行的话就不需要去解释执行了,因此运行效率会更高,也会更省电,因为程序运行的时候不用重复编译了,减少了CPU的使用。缺点是安装APK的时候速度慢,就是因为多了AOT的步骤,把dex文件编译成了机器语言消耗了时间。另外ART是兼容Dalvik的。如果运行程序的时候使用的是OAT文件那么就按照ART正常的流程去执行。如果在运行的时候加载的是dex文件,那么就和Dalvik一样需要JIT和解释执行。
1.2 Dexopt和Dexaot
Dexopt:在Dalvik模式下,当dex文件被加载到Dalvik虚拟机中需要对dex文件进行验证和优化得到一个odex文件。(optimized dex)
Dexaot:在ART模式下,前两步和DexOpt是一样的,区别就是在变成odex之后需要进行AOT提前编译,编译成OAT文件
2.1Android中的ClassLoader

Android和Java中的ClassLoader是有区别的。Android加载的是dex文件,Java加载的则是class文件。其中需要重点关注的是PathClassLoader。其实不管是PathClassLoader还是DexClassLoader都只是继承BaseDexClassLoader并且重写了构造方法,没有增加其他的逻辑。
public DexClassLoader(String dexPath,String optimizedDirectory,String libraryPath, ClassLoader parent){
super(dexPath,new File(optimizedDirectory),libraryPath, parent);
}
public PathClassLoader(String dexPath,String libraryPath, ClassLoader parent){
super(dexPath,null,libraryPath, parent);
}
可以看到两者的区别就在于第二个参数。DexClassLoader需要传入一个优化的路径,也就是存放odex的路径。需要注意的是优化的路径不是随意的,必须是程序的私有目录,sdcard不可以。而PathClassLoader则传入了null。传了null不代表不优化,而是用了应用程序的私有目录/data/dalvik-cache下。
2.2 双亲委托机制
定义:某个类加载器在加载类时,首先将加载任务交给父加载器,依次递归,如果父加载器可以完成加载任务就返回,如果父加载器失败或者没有父加载器才自己去加载。(这里的父加载器不是指的BaseDexCLassLoader而是指的构造参数当中的ClassLoader parent)
ClassLoader.class
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
// 根据名字查找之前是否已加载过这个类
Class<?> c = findLoadedClass(name);
// 之前没加载过
if (c == null) {
try {
if (parent != null) {
// 委托父加载器进行加载
c = parent.loadClass(name, false);
} else {
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e){
}
// 父加载器没找到
if (c == null) {
// 自己去找
c = findClass(name);
}
}
return c;
}
// 在ClassLoader这个类中的findClass方法只是扔了一个异常,并没有实现,在子类当中实现了
protected Class<?> findClass(String name) throws ClassNotFoundException {
throw new ClassNotFoundException(name);
}
//------------------------------------分割线----------------------------------------
BaseDexClassLoader.class
public BaseDexClassLoader(String dexPath, File optimizedDirectory, String librarySearchPath, ClassLoader parent) {
super(parent);
// 构建DexPathList对象
this.pathList = new DexPathList(this, dexPath, librarySearchPath ,optimizedDirectory);
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
List<Throwable> suppressedExceptions = new ArrayList<Throwable>();
// 真正执行findClass的是DexPathList
Class c = pathList.findClass( name, suppressedExceptions);
if(c == null){
ClassNotFoundException cnfe = new ClassNotFoundException("Didn't find class \"" + name + "\" + on path " + pathList";
for(Throwable t : suppressedExceptions){
cnfe.addSuppressed(t);
}
throw cnfe;
}
return c;
}
//------------------------------------分割线----------------------------------------
DexPathList.class
public Class<?> findClass(String name, List<Throwable> suppressed) {
// 从Element[] 数组当中遍历寻找
// 而Element[]是由方法下面的makeDexElements生成的
for (Element element : dexElements) {
Class<?> clazz = element.findClass(name, definingContext, suppressed);
if (clazz != null) {
return clazz;
}
}
if (dexElementsSuppressedExceptions != null) {
suppressed.addAll(Arrays.asList(dexElementsSuppressedExceptions));
}
return null;
}
/**
* files : 所有的dex文件
* optimizedDirectory :优化的目录
* suppressedExceptions :异常
* loader :类加载器
*/
private static Element[] makeDexElements(List<File> files, File optimizedDirectory,
List<IOException> suppressedExceptions, ClassLoader loader) {
Element[] elements = new Element[files.size()];
int elementsPos = 0;
for (File file : files) {
if (file.isDirectory()) {
elements[elementsPos++] = new Element(file);
} else if (file.isFile()) {
String name = file.getName();
// 文件是否以 .dex结尾
if (name.endsWith(DEX_SUFFIX)) {
try {
// 调用loadDexFile来加载dex文件
DexFile dex = loadDexFile(file, optimizedDirectory, loader, elements);
if (dex != null) {
// 把DexFile对象传入到Element对象中
elements[elementsPos++] = new Element(dex, null);
}
......
return elements;
}
private static DexFile loadDexFile(File file, File optimizedDirectory, ClassLoader loader,
Element[] elements)
throws IOException {
if (optimizedDirectory == null) {
return new DexFile(file, loader, elements);
} else {
String optimizedPath = optimizedPathFor(file, optimizedDirectory);
return DexFile.loadDex(file.getPath(), optimizedPath, 0, loader, elements);
}
}
//所以最终在上面遍历Element的findClass最终实际是调用了DexFile内部的native函数来找到需要加载的类的。
经过了上面的层层调用可以发现一个转换的步骤就是:dex->DexFile->Element。所以可以把Element当作一个dex文件。Element[]就是一个dex数组。接着循环遍历Element数组来找到要加载的类,那么只需要在数组的前面插入我们要修复的类的dex就可以达到修复的目的了。如图所示:

网友评论