很早之前就想深入的研究和学习一下热修复,由于时间的原因一直拖着,现在才执笔弄起来。


Android而更新系列:
Android热更新一:JAVA的类加载机制
Android热更新二:理解Java反射
Android热更新三:Android类加载机制
Android热更新四:热修复机制
Android热更新五:四大热修复方案分析
Android热更新六:Qzone热更新原理
Android热更新七:Tinker热更新原理
Android热更新八:AndFix热更新原理
Android热更新九:Robust热更新原理
Android热更新十:自己写一个Android热修复


既然本次主题是热修复,在深入研究之前,要先搞清楚它涉及到的关键技术

类加载机制
Java反射

正所谓磨刀不误砍柴工,我们这个笔记将对Android类加载机制进行初步学习和理解。

学习Android类加载机制之前,我们先要了解JAVA的类加载机制和JAVA反射。

一. 虚拟机与加载器

我们知道Android系统也是仿照java搞了一个虚拟机,不过它不叫JVM,它叫Dalvik/ART VM。

Dalvik/ART VM 虚拟机加载类和资源也是要用到ClassLoader,不过Jvm通过ClassLoader加载的class字节码,而Dalvik/ART VM通过ClassLoader加载则是dex。

Android的类加载器分为两种,PathClassLoader和DexClassLoader,两者都继承自BaseDexClassLoader

PathClassLoader代码位于libcore\dalvik\src\main\Java\dalvik\system\PathClassLoader.java
DexClassLoader代码位于libcore\dalvik\src\main\java\dalvik\system\DexClassLoader.java
BaseDexClassLoader代码位于libcore\dalvik\src\main\java\dalvik\system\BaseDexClassLoader.java

PathClassLoader

用来加载系统类和应用类

DexClassLoader

用来加载jar、apk、dex文件.加载jar、apk也是最终抽取里面的Dex文件进行加载.

二. Dalvik虚拟机类加载流程

Dalvik虚拟机毕竟不算是标准的Java虚拟机,因此在类加载机制上,Dalvik虚拟机与Java虚拟机有许多不同之处,例如,在使用标准Java虚拟机时,我们经常自定义继承自ClassLoader的类加载器。然后通过defineClass方法来从一个二进制流中加载Class。然而,这在Dalvik虚拟机上是行不通的。

Dalvik虚拟机类加载流程如下图所示:


三. Dalvik虚拟机类加载器源码分析

Android的类加载器主要有两个PathClassLoader和DexClassLoader,其中PathClassLoader是默认的类加载器,下面我们就来说说两者的区别与联系。

  • PathClassLoader:支持加载DEX或者已经安装的APK(因为存在缓存的DEX)。
  • DexClassLoader:支持加载APK、DEX和JAR,也可以从SD卡进行加载。

DexClassLoader和PathClassLoader都属于符合双亲委派模型的类加载器(因为它们没有重载loadClass方法)。也就是说,它们在加载一个类之前,回去检查自己以及自己以上的类加载器是否已经加载了这个类。如果已经加载过了,就会直接将之返回,而不会重复加载。

PathClassLoader还是DexClassLoader继承于BaseDexClassLoader,BaseDexClassLoader继承鱼ClassLoader,下面我们就以一个类的加载流程来分析各个加载器的源码实现,详细的源码在下方附录中给出。

要加载一个类,必须先初始化一个类加载器实例,我们拿DexClassLoader来举例,它的构造方法如下所示:

    public DexClassLoader(String dexPath, String optimizedDirectory,            String libraryPath, ClassLoader parent) {        super(dexPath, new File(optimizedDirectory), libraryPath, parent);    }

该函数中的参数含义如下所示:

  • String dexPath:加载APK、DEX和JAR的路径。这个类可以用于Android动态加载DEX/JAR。
  • String optimizedDirectory:是DEX的输出路径。
  • String libraryPath:加载DEX的时候需要用到的lib库,libraryPath一般包括/vendor/lib和/system/lib。
  • ClassLoader parent:DEXClassLoader指定的父类加载器

关于DexClassLoader,除了它的构造函数以外,它的源码注释里还提到以下三点:

    1. 这个类加载器加载的文件是.jar或者.apk文件,并且这个.jar或.apk中是包含classes.dex这个入口文件的,主要是用来执行那些没有被安装的一些可执行文件的。
    1. 这个类加载器需要一个属于应用的私有的,可以的目录作为它自己的缓存优化目录,其实这个目录也就作为下面,这个构造函数的第二个参数,至于怎么实现,注释中也已经给出了答案;
    1. 不要把上面第二点中提到的这个缓存目录设为外部存储,因为外部存储容易收到代码注入的攻击。

通过DexClassLoader的构造函数,我们可以发现DexClassLoader的构造函数会调用父类的构造函数进行初始化,DexClassLoader的父类就是BaseDexXClassLoader,我们继续来看一下BaseDexClassLoader的构造函数:

    public BaseDexClassLoader(String dexPath, File optimizedDirectory,            String libraryPath, ClassLoader parent) {        super(parent);        this.pathList = new DexPathList(this, dexPath, libraryPath, optimizedDirectory);    }

我们可以发现在执行BaseDexClassLoader的构造函数时,会先调用父类ClassLoader的构造方法:

    /**     * Constructs a new instance of this class with the system class loader as     * its parent.     */    protected ClassLoader() {        this(getSystemClassLoader(), false);    }    /**     * Constructs a new instance of this class with the specified class loader     * as its parent.     *     * @param parentLoader     *            The {@code ClassLoader} to use as the new class loader's     *            parent.     */    protected ClassLoader(ClassLoader parentLoader) {        this(parentLoader, false);    }    /*     * constructor for the BootClassLoader which needs parent to be null.     */    ClassLoader(ClassLoader parentLoader, boolean nullAllowed) {        if (parentLoader == null && !nullAllowed) {            throw new NullPointerException("parentLoader == null && !nullAllowed");        }        parent = parentLoader;    }

通过ClassLoader的构造函数源码可以发现,BaseDexClassLoader里的parentLoader对象经过层层传递,传递给了parent对象,parent对象是ClassLoader类里的私有变量,如下所示:

  /**     * The parent ClassLoader.     */    private ClassLoader parent;

这一步做完以后,BaseDexClassLoader的构造函数紧接着就初始化了一个DexPathList对象,这是一个描述DEX文相关资源文件的条目列表。
接下来看下DexPathList的构造函数

    public DexPathList(ClassLoader definingContext, String dexPath,            String libraryPath, File optimizedDirectory) {        if (definingContext == null) {            throw new NullPointerException("definingContext == null");        }        if (dexPath == null) {            throw new NullPointerException("dexPath == null");        }        if (optimizedDirectory != null) {            if (!optimizedDirectory.exists())  {                throw new IllegalArgumentException(                        "optimizedDirectory doesn't exist: "                        + optimizedDirectory);            }            if (!(optimizedDirectory.canRead()                            && optimizedDirectory.canWrite())) {                throw new IllegalArgumentException(                        "optimizedDirectory not readable/writable: "                        + optimizedDirectory);            }        }        this.definingContext = definingContext;        ArrayList suppressedExceptions = new ArrayList();        this.dexElements = makeDexElements(splitDexPath(dexPath), optimizedDirectory,                                           suppressedExceptions);        if (suppressedExceptions.size() > 0) {            this.dexElementsSuppressedExceptions =                suppressedExceptions.toArray(new IOException[suppressedExceptions.size()]);        } else {            dexElementsSuppressedExceptions = null;        }        this.nativeLibraryDirectories = splitLibraryPath(libraryPath);    }

可以看到DexPathList里比较重要dexElements也是在这里被初始化,热修复就是倒腾这个东东做到修复bug的。

 this.dexElements = makeDexElements(splitDexPath(dexPath), optimizedDirectory, suppressedExceptions);

更多相关文章

  1. Android热更新十:自己写一个Android热修复
  2. 第5章 Android常见XML属性解析-更新中
  3. 一些小效果(持续更新……)
  4. Android(安卓)SDK Android(安卓)NDK 官方下载地址(更新加版本号
  5. 【学习推荐】最新Android学习教程持续更新
  6. Android(安卓)sdk开发工具下载(已更新)
  7. Android(安卓)SDK Android(安卓)NDK 官方下载地址(更新加版本号
  8. element ui级联选择器--懒加载数据
  9. Manjaro Linux 入门使用教程

随机推荐

  1. Writing an Android Sync Provider: Part
  2. Android Html类
  3. Android异常解决--A WebView method was
  4. Bluebox Security最新提报Android漏洞的
  5. Android Studio开发应用
  6. Android中文API (39) —— AbsSpinner
  7. Activity的属性
  8. Android: eoeAndroid ~
  9. android 知识积累
  10. 写android简易播放音乐遇到的问题