深入理解android类加载原理(热更新)

安卓虚拟机

Dalvik:Dalvik是Google公司用于android平台的java虚拟机。支持已转化为.dex格式的java应用程序的运行。.dex格式是专门为Dalvik应用设计的一种压缩格式,适合内存和处理器速度有限的系统。

ART:Android Runtime,Android 4.4中引入的一个开发者选项,也是Android 5.0及更高版本的默认模式。在应用安装的时候Ahead-Of-Time(AOT)预编译字节码到机器语言。这一机制叫AOT预编译。应用程序安装会变慢,但是执行将更有效率,启动更快。

在Dalvik下,应用程序需要解释运行,常用热点代码通过即时编译器(JIT)将字节码转换为机器码,运行效率低。在ART环境中,应用在安装时,字节码预编译(AOT)成机器码,安装慢了,但是运行效率会变高。

格式

ART会执行AOT,但在Dalvik开发的应用也可以在ART环境下运作。
dexopt:对dex文件进行验证和优化生成odex(Optimized dex)文件
dexAot:在安装时对dex文件文件进行dex优化后为odex文件再进行AOT提前编译操作,编译为OAT可执行文件(机器码)。

ClassLoader

继承关系如图:


图片.png

` BootClassLoader:
用于加载Android Framework层的class文件。

` PathClassLoader:
用与android应用程序内的类加载器。可以加载指定的dex,以及jar/zip/apk中的classes.dex,主要的实现实在BaseDexClassLoader中。

` DexClassLoader
加载指定的dex,以及jar/zip/apk中的classes.dex,主要的实现实在BaseDexClassLoader中。

在任意的Activity加入如下代码

  ClassLoader classLoader = getClassLoader();
        ClassLoader classLoader1 = Activity.class.getClassLoader();

        System.out.println("getClassLoader:"+classLoader);
        System.out.println("getClassLoader 的父亲 :"+classLoader.getParent());
        System.out.println("Activity.class :"+classLoader1);

运行结果

getClassLoader:dalvik.system.PathClassLoader[DexPathList[```]]
getClassLoader 的父亲 :java.lang.BootClassLoader@1d467fc
Activity.class :java.lang.BootClassLoader@1d467fc

双亲委托机制

某个类加载器在加载类时,首先将加载任务委托给parent加载器。依次递归,如果parent加载器可以完成类加载任务,就成功返回;只有parent加载器无法加载任务或者没有parent加载器时,才自己去加载。
分析:loadClass在ClassLoader和BootClassLoader才有实现。

protected Class loadClass(String className, boolean resolve) throws ClassNotFoundException {
      //如果被加载过,直接返回,这就是热更新的原理。
      //将新的dex文件传到dexPathList前面。
        Class clazz = findLoadedClass(className);

        if (clazz == null) {
            ClassNotFoundException suppressed = null;
            try {
              //递归调用父亲的loadClass
                clazz = parent.loadClass(className, false);
            } catch (ClassNotFoundException e) {
                suppressed = e;
            }

            if (clazz == null) {
                try {
                  //父亲找不到,则自己去加载
                    clazz = findClass(className);
                } catch (ClassNotFoundException e) {
                    e.addSuppressed(suppressed);
                    throw e;
                }
            }
        }

        return clazz;
    }

看看BootClassLoader的loadClass():

protected Class loadClass(String className, boolean resolve)
           throws ClassNotFoundException {
        //找到后直接返回
        Class clazz = findLoadedClass(className);

        if (clazz == null) {
          //为空则自己去加载
            clazz = findClass(className);
        }

        return clazz;
    }

可以看出,BootClassLoader重写了loadClass方法,这里没有parent属性。

具体思路

1.首先拿到BaseDexClassLoader里的pathList属性。

···示例代码
Field pathList= classLoader.getDeclaredField("pathList");

2.找到pathList的makePathElements()方法并调用,生成新的dexElements;

···示例代码
 Method method = clazz.getDeclaredMethod("makeDexElements", parameterTypes);


private static Element[] makeDexElements(List files,File optimizedDirectory,
List suppressdExceptions,classLoader loader,boolean is Trussed){
    Element[] elements = new Element[file.size];
}

makeDexElements()中,files就是dex文件。

3.将原本的 dexElements 与 makePathElements生成的数组合并,修改dexElement的值

//合并后的数组
Object[] combined ;
System.arraycopy(NewElements, 0, combined, 0, NewElements.length);
        System.arraycopy(OldElements, 0, combined, NewElements.length, OldElements.length);

你可能感兴趣的:(深入理解android类加载原理(热更新))