本文通过学习聊聊 APK —— Dex 热修复与 Classpath 总结
在将java文件直接运行在JVM和Dalvik中,中讲解了java程序如何在JVM和Dalvik中运行,其中在Android部分实现是,先将java文件转为dex文件,导入至安卓手机中,通过Dalvik对该文件编译运行,实现将java文件运行在手机中功能。本文重点讲解如何利用该功能实现简易热修复的效果。
热修复的根本原理是通过修改DexClassLoader 中dexPathList 的dex顺序,达到热修复的效果。
首先创建一个带有打印bug的类test.java,如下所示:
package xiaohan;
public class Test {
public void test (){
System.out.println("This a BUG!");
}
}
这里需要注意,由于此类需要在带有main的类中引用,需要自定义一个package,否则无法正常引用该类。
创建一个带有main的主类HelloWorld.java,该类中需要引用Test,通过import的形式引入。
import xiaohan.Test;
public class HelloWorld {
public static void main (String [] args){
Test test = new Test();
test.test();
}
}
由于需要加载两个java文件,需要通过以下语句执行,注意cp之后有个 “.”的修饰符。
javac -cp . HelloWorld.java Test.java
将生成的两个class文件,生成一个dex文件,这例需要注意,如果直接按照之前的语句:
dx --dex --output=xiaohan.dex HelloWorld.class Test.class
则会出现以下错误:
E:\ProgramFiles\Android\build-tools\28.0.3>dx --dex --output=xiaohan.dex HelloWorld.class Test.class
PARSE ERROR:
class name (xiaohan/Test) does not match path (Test.class)
...while parsing Test.class
1 error; aborting
原因是由于在创建Test.java文件时,为其指定了包名为xiaohan,与HelloWorld不在同一包下,因此需要创建一个xiaohan的文件夹,将Test.class拷贝进去,然后执行如下语句:
dx --dex --output=xiaohan.dex HelloWorld.class xiaohan/Test.class
就会生成一个的xiaohan.dex文件,将其拷贝至手机后,运行如下语句:
dalvikvm -cp xiaohan.dex HelloWorld
重新创建一个新的test.java文件,修改其内容如下:
package xiaohan;
public class Test {
public void test (){
System.out.println("Fix a BUG!");
}
}
制作一个新的class文件,注意只需要制作Test.class文件,与HelloWorld.class无关,制作语句如下,去除“.”,包含HelloWorld因为在编译时会报错。
javac -cp HelloWorld.java Test.java
将新制作的Test.class文件制作成功独立的dex文件,这里命名为new.dex,通过执行如下语句:
dx --dex --output=new.dex xiaohan/Test.class
该dex的是制作补丁包,只包含test.class文件,不包含HelloWorld.class文件。
将生成的new.dex文件,推送至手机存放HelloWorld.dex的目录下,执行修复后的语句,如下所示。
dalvikvm -cp new.dex:xiaohan.dex HelloWorld
注意,这里需要将补丁包放置前面,然后再添加xiaohan.dex(包含main的dex文件),否则如果先添加new.dex或者不添加,则无法到达修复效果,如下语句。
dalvikvm -cp xiaohan.dex:new.dex HelloWorld
以上只是简单的演示在Dalvik虚拟机中,通过修改DexClassLoader 中dexPathList 的dex加载顺序,实现修复的效果,实际使用中,一般我们的APP是安装在data/data/包名/ 目录下, 如果需要热修复文件,在制作好补丁文件(补丁.dex文件)后推送至应用端,在应用使用时,判断是否有该补丁文件,再通过反射形式,修改dexPathList中补丁包的顺序,实现APP热修复的效果。可参考Android学习——手把手教你实现Android热修复