安卓手写热修复demo

安卓手写热修复demo

目录

用[TOC]`来生成目录:

预备知识

了解安卓如何加载类:Android插件化开发之动态加载基础之ClassLoader工作机制

demo:码云地址

在预备知识中可以了解到安卓初次加载类调用链如下
PathClassLoader.findClass->DexPathList.findClass->Element.loadClassBinaryName
并且当一个类加载到内存后,将不会再次加载。
本文的修复原理是修改DexPathList中的dexElements,让系统优先加载热修复包中的类达到修复功能
提示:本文只涉及原理与简单实现,并未考虑实际开发适配等等问题

实现步骤

1、获取系统dexElements

1.系统的dexElements在PathClassLoader中PathList的成员变量
2.在Context中 getClassLoader即可获取PathClassLoader。
3.代码如下

classLoader classLoader= getClassLoader();
Field pathListField = ReflectUtil.findFiled(classLoader.getClass(), "pathList");//通过反射获取ClassLoader中pathList成员变量Filed
Object pathList = pathListField.get(classLoader);
Field dexElementsFiled = ReflectUtil.findFiled(pathList.getClass(), "dexElements");//通过反射获取DexPathList中dexElements成员变量Filed
Object dexElements = dexElementsFiled.get(pathList);

ReflectUtil为反射工具类

2、加载外部Dex文件

1.加载外部dex文件此处用,DexClassLoader

DexClassLoader dexClassLoader = new DexClassLoader(filePath,
                optimizedDirectory.getAbsolutePath(), null, ClassLoader.getSystemClassLoader());

此处重要的参数为前两个
第一个为dex文件路径,第二个必须为一个可读写的文件夹路径。其他的在预备知识有了解不多赘述。
在实例化DexClassLoader后,当前DexClassLoader中dexElements将会被赋值。
再次类似通过获取系统dexElements一样获取dexClassLoader中dexElements成员。

3、将外部dex文件中的dexElements与系统dexElements合并

现在获取到了系统的dexElements为 pathElements,外部加载dexElements为outElements
由于系统加载类会按dexElement的数组顺序查找类,在合并时需要将outElements放在pathElemnets前面才能优先被系统加载

Object arrayAppend = arrayAppend(outElements, pathElemnets);//将outElements, pathElemnets合并成一个数组
 Field pathListField = ReflectUtil.findFiled(classLoader.getClass(), "pathList");
Object pathList = pathListField.get(classLoader);
Field dexElementsFiled = ReflectUtil.findFiled(pathList.getClass(), "dexElements");
dexElementsFiled.set(pathList, arrayAppend);//将系统dexElements修改为合并后的数组即可

此修复方法在类已经加载过不能达到修复效果,在修复需要在application中修复。

修复效果测试

定义测试

1、新建一个类

public class Function {

    public void test(){
        throw new RuntimeException("sss");
    }
}

2、在MainActivity中设置一个按钮的点击监听为如下

public void click(View view) {
    Function function = new Function();
    function.test();
}

建立热修复dex文件

在测试时,用Function.class生成dex文件总是报如下错:
PARSE ERROR:
class name (com/spyfool/hotfixtdemo/Function) does not match path (C:/Users/Admi
nistrator/Desktop/dex/com/spyfool/hotfixtdemo/Function.class)
…while parsing C:/Users/Administrator/Desktop/dex/com/spyfool/hotfixtdemo/Func
tion.class。具体原因未知,望有人能解答一下

新建一个Library包,包名跟appv包名相同。新建Function类

public class Function {

    public void test(){
       //此处只要表现跟app中Function表现不一致即可用于验证修复是否成功
    }
}

目录:工程目录\mylibrary(library目录)\build\outputs\aar\mylibrary-debug.aar。将此aar按后缀改成zip,并解压可以得到jar文件,将此jar文件转成dex文件即可用于修复。如果没有aar文件,rebuild project即可
转换命令: dx –dex –output [生成dex文件绝对路径 例:D:/d.dex] [jar文件绝对路径]
其中 dx命令需要配置变量。或者使用dx文件绝对路径也可 例: E:\sdk\build-tools\26.0.3\dx.bat –dex –output E:\d.dex E:\classes.jar

以上内容如有问题请指正。

涉及资料:
Android插件化开发之动态加载基础之ClassLoader工作机制

发布了9 篇原创文章 · 获赞 5 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/spyunknow/article/details/79369019