Android Classloader热修复

时间:2021-7-20 作者:qvyue

惯例段子。

Android Classloader热修复
哈哈哈嗝
阅读本文你可以掌握,热修复的原理和简单实现.
目录
Classloader热修复原理
热修复代码实现
面试知识

Classloader热修复原理

Android Classloader热修复
classloaderDemo

从这个图上能看出什么?

1,PathClassLoader 是Android 默认加载器.
2,BootClassLoader是PathClassLoader 的父加载器(注意不是父类).

原理

1,上一篇classLoader文章介绍过PathClassLoader加载类的过程拿到pathList对象,然后拿到里面的Element数组,进行类的加载.
2,双亲委派机制,如果一个类之前加载过就不会再被加载了.

这两点就是ClassLoader热修复的核心原理.

把修复的dex文件,放到Element数组前边,根据双亲委派机制就会加载修复之后的dex文件了,有问题的dex文件因为双亲委派机制就不会加载了.(本来想画个图,但是画的太丑就没上图)

热修复代码实现

思路

1,PathClassLoader 加载路径我们不能修改,但是DexClassLoader 我们是可以指定加载路径的,这里不做深层说明了,底层根据那个参数去判断的加载目录.(可能这个说法不太对,但是都是通过dexClassloader去做的,有兴趣的自行了解)
2,拿到PathClassLoader 的Element数组,再拿到我们定义的DexClassLoader 的 Element数组,然后把数组合并一下,把我们DexClassLoader 的Element数组放在前边,再把最后的Element数组给PathClassLoader 设置回去,就实现了热修复.
上代码


public class HotFixTest {

    public void test() throws NullPointerException {
       throw  new NullPointerException();
    }
}
public class Secondectivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_secondectivity);
        findViewById(R.id.click_View2).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                new HotFixTest().test();
            }
        });

    }
}

这个是需要修复的类,现在我们抛了一个异常,这个代码都能看懂吧 一点击就会抛出空指针异常

public class HotFixTest {

    public void test() throws NullPointerException {
        Toast.makeText(MyApplication.getContext(),"修复啦 ",Toast.LENGTH_SHORT).show();
    }
}

1,这个是我们需要替换的类,修改之后现在把它打成dex文件.可以用javac或者Android 首先编译程.class文件

2,然后把class文件通过dx指令生成dex文件
第一种配置环境变量
第二种直接目录下执行,直接在build-tools/安卓版本 目录下使用命令行窗口使用。

dx –dex –output=输出的dex文件完整路径 (空格) 要打包的完整class文件所在目录

Android Classloader热修复
class.png

包名一定要跟你定义的包名一致.
dx –dex –output=C:Users76209Desktopdexpach.dex C:Users76209Desktopclass

Android Classloader热修复
dx.png

3,转换成dex文件之后,我们需要通过下载啊或者其他方式放到包名下能够加载到的地方
这里用adb命令

adb push C:Users76209Desktopdexpach.dex /storage/emulated/0/Android/data/com.example.lenkdlist/files/patch/patch.dex

Android Classloader热修复
adb.png

然后可以从包名下看到dex文件

Android Classloader热修复
手机.png

4,核心代码要来了

public class XHotFixUtil {

    public static void fix() throws IllegalAccessException, NoSuchFieldException, ClassNotFoundException {
        //拿到补丁dex的文件路径
        File dexSrc = MyApplication.getContext().getExternalFilesDir("patch");
        if (!dexSrc.exists()) {
            return;
        }
        //遍历文件夹找出dex,jar,apk结尾的文件
        File[] fileList = dexSrc.listFiles();
        StringBuffer stringBuffer = new StringBuffer();
        for (int i = 0; i  baseClazz = Class.forName("dalvik.system.BaseDexClassLoader");
        Field pathList = baseClazz.getDeclaredField("pathList");
        pathList.setAccessible(true);
        Object pathListObj = pathList.get(classLoader);
        Field dexElements = pathListObj.getClass().getDeclaredField("dexElements");
        dexElements.setAccessible(true);
        Object o = dexElements.get(pathListObj);
        return o;
    }

    /**
     * 合并两个  elements数组
     * @param elements1
     * @param elements2
     * @return
     */
    public static Object merge(Object elements1,Object elements2){
        Class> componentType = elements1.getClass().getComponentType();//拿到数组的泛型
        int length1 = Array.getLength(elements1);
        int length2 = Array.getLength(elements2);
        int total = length1 +length2;
        //数组创建不能指定泛型
        Object mergeElements = Array.newInstance(componentType, total);
        System.arraycopy(elements2, 0, mergeElements, 0,length2);
        System.arraycopy(elements1, 0, mergeElements, length2,length1);
        return  mergeElements;
    }

    /**
     * 设置给默认类的加载器
     * @param classLoader
     * @param elements
     * @throws NoSuchFieldException
     * @throws IllegalAccessException
     * @throws ClassNotFoundException
     */
    public static void setElements(ClassLoader classLoader,Object elements) throws NoSuchFieldException, IllegalAccessException, ClassNotFoundException {
        Class> baseClazz = Class.forName("dalvik.system.BaseDexClassLoader");
        Field pathList = baseClazz.getDeclaredField("pathList");
        pathList.setAccessible(true);
        Object pathListObj = pathList.get(classLoader);
        Field dexElements = pathListObj.getClass().getDeclaredField("dexElements");
        dexElements.setAccessible(true);
        dexElements.set(pathListObj,elements);
    }
}

面试知识

1,双亲委派机制,注意是包含,并不是继承,充分了解机制
2,利用创建dexClassLoader 对象把新的dex文件加载进去
3,patchList对象的elements数组,利用双亲委派机制进行,数组合并.
4,这只是其中一类热修复的原理,还有底层替换,instance run原理(使用 ASM 在每一个方法 中注入了代码)

声明:本文内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:qvyue@qq.com 进行举报,并提供相关证据,工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。