Android热补丁动态修复技术设计与实现(3)

字节码注入模块:在class文件打包成Dex之前进行一些字节码注入,避免Android系统对Dex进行Odex化,方便之后dex插入操作; 推送模块:链接所有的Android设备,


字节码注入模块:在class文件打包成Dex之前进行一些字节码注入,避免Android系统对Dex进行Odex化,方便之后dex插入操作;

推送模块:链接所有的Android设备,在发布补丁包的时候,针对终端进行推送;

补丁包管理模块:检查手机内是否有推送的补丁包,检测到补丁包时统一管理;

dex插入模块:通过反射操作,调转ClassLoader的读取Class顺序,使得补丁包首先加载,达到更新的目的;

1.3 相关技术介绍

1.3.1 Java简介

Java是1996年由Sun公司发布的面向对象的编程语言。其中中包含了C++语言的多种优点,但抛弃了其中中一些难以理解的部分,类似多继承、指针等。同时Java语言也是一门很好的面向对象语言,允许程序员以面向对象的方式进行编程。

1.3.2 Groovy简介

Groovy是运行于Java虚拟机上的一种语言,其也拥有和Python相似的很多特性。由于其可以运行在 JVM 上,使Groovy 代码能够与 Java 代码互相调用,结合编码,同样Groovy代码能够调用 Java 语言编写的库。在Groovy语言设计时很好的完成了对Java语言的集成,方便两种语言的结合编程。

1.3.3 JAVAssist库

Javassist是一个开源类库,它被用于解析、修改Java字节码。除了Javassist以外还有例如bcel,asm这些可以用于字节码处理,但是使用这些库都需要了解虚拟机指令,因为在库的使用中都牵扯到了部分虚拟机指令。但是javassist库对使用者隐藏了虚拟机指令部分。如果使用javassist库就可以跨过虚拟机指令,使用java编码对字节码文件进行解析、编辑java字节码。可以对字节码文件做很多特殊操作。

1.3.4 dex插入技术

在java虚拟机中,ClassLoader加载了每一个需要的类。而在dalvik虚拟机中没有class文件,类似于class文件的是dex文件。dex文件由多个class文件组成,这种优化可以增加运行效率。而每一个dex文件在虚拟机中被封装成一个Element被存放在ClassLoader的有序数组dexElements变量中。当需要加载一个类的时候,会按照数组的排列顺序遍历类加载器的变量dexElements,然后从当前所在的dex文件中找查找需要加载的类,如果找到需要加载的类则返回这个类,否则就转到下一个dex继续向查找。所以,如果有有相同的类存在于不同的dex中,那么会优先选择先遍历到的dex文件中的类。因此可以插入一个新的dex到有序数组dexElements的最最前端,就可以实现dex的插入。从而覆盖原有的类,达到动态替换的效果。

图1-1 ClassLoader加载Class图

1.3.5 防止CLASS_ISPREVERIFIED化技术

在应用安装时,虚拟机会对应用包中的classes.dex进行优化,优化完成后会形成odex文件,然后执行这些类。

在整个应用的优化过程中,会调用dvmVerifyClass()函数对类进行校验,如果校验类成功,那么会把CLASS_ISPREVERIFIED标志添加在这个类上,代表这个类校验成功。对于校验成功的类在,运行时不允许引用其他dex中的class。如果这个类在运行时调用了其他dex中的类,这个应用就会发生错误,导致整个应用崩溃。在启动虚拟机时,会传入许多的启动参数,其中有一个verify选项,它控制了是否进行类的校验,但是实际情况中不允许我们修改这个参数,所以就需要考虑其他办法。

如果在应用安装时,classes.dex中的类都引用了其他dex中的类时,在执行dvmVerifyClass()函数进行类的校验时,因为这个类引用了其他dex包中的类,所以CLASS_ISPREVERIFIED标志不会被添加到这个类中。但是这样会在一定程度上引起运行效率下降。

图1-2 CLASS_ISPREVERIFIED标记crash 图

因为热补丁的实际需要,所以需要在应用安装时就必须防止类被打上CLASS_ISPREVERIFIED标记。解决方案就是对应用dex中所有的类引用与这个类不同dex的另一个类。这样在安装检测的时候,会因为在这个类中引用了其他dex中的类,CLASS_ISPREVERIFIED的标志不会被添加到这个类中。