源码学习《7》Classloader 类加载机制 (热修复 2)实现篇

通过上一篇对Classloader的分析,对classloader的原理有了初步的了解。今天就在了解的基础上实现类的热修复。在做热修复之前首先我们来看看 MutilDexEnable 这个属性。

1. MutilDexEnable 指定class文件到dex中

我们知道如果我们项目很大,方法超过了65535 个方法数,就需要使用分包处理,大致是这样的

android {
    defaultConfig {
        // 设置支持multidex
        multiDexEnabled true
    }   
}
dependencies {
    //引入对multidex支持的库	
    compile 'com.android.support:multidex:1.0.1'
}

但是这种是默认给我们添加一个class2.dex文件,但是我们没有控制那些class文件放到那一个dex中。现在我们在热修复中由于需要动态的把dex中的有bug的class文件替换掉,因此我们可以把我们apk中的dex文件分成主dex和辅dex文件。主dex文件保存我们启动时的少量class文件即可,这样我们就可以利用主dex文件来修复辅dex文件下面的bug class了。

对于分包我们上面代码已经完成了,接下来就是指定class文件到主dex了。

首先要配置一个mutildex.txt文件,里面存放我们指定的class文件。然后在build.gradle中引用。

参考:https://blog.csdn.net/qq_36282231/article/details/81026296

android {
    defaultConfig {
        // 设置支持multidex
        multiDexEnabled true
    }  
    //新增的配置信息
    dexOptions{
	preDexLibraries = false
	additionalParameters = [    //配置multidex参数
	                                '--multi-dex',//多dex分包
	                                '--set-max-idx-number=30000',//每个包内方法数上限
	                                '--main-dex-list='+projectDir+'/mutildex.txt', //打包到主classes.dex的文件列表
	                                '--minimal-main-dex'
	                            ]
	}
}
dependencies {
    //引入对multidex支持的库	
    compile 'com.android.support:multidex:1.0.1'
}

然后在mutildex.txt文件中加入我们指定的class

最后打开apk文件得到的class.dex

可以看到这两个文件在同一个dex中了。接下来就开始如正题。

2. 编写修复代码

 首先我们要明白我们需要做什么,目的是为了修改系统的 DexPathList 中的 dexElements 数组。说明:

  1. 通过Classloader源码的解读,了解了apk中的 dex 文件都会保存到 dexElements 中,并且是按照顺序加载的,一个class被加载之后就不会再去加载相同的class文件,所以我们可以把修复好的dex提前放入数组,等待动态加载。
  2. 由于我们带有bug的class文件也是被放到辅 dex 中的(尽量保证我们的主 dex 不出现bug,否则无法修复)
  3. 所以我们就需要把我们修改bug后生成的dex差分包插入到dexElements数组最前面。

明白了流程,接下来看代码

public void fixHot(Context context,String path){
        // 修改了bug的dex 差分包的path
        File file = new File(path);
        if (!file.exists()){
            Log.e(TAG,"----fixHot---exists");
            return;
        }
        ClassLoader loader = context.getClassLoader();
        List<File> files = new ArrayList<>();
        files.add(file);
        File optimized = context.getCacheDir();
        Log.e(TAG,"----fixHot---" + optimized);
        /**
         * Field
         * Method
         * Class
         */
        try {
            // 反射拿到pathList field,由于这个属性是在父类中的,所以要通过 getSuper 拿到父类
            Field pathlist = loader.getClass().getSuperclass().getDeclaredField("pathList");
            // 由于这个属性是私有的,改成可访问权限
            pathlist.setAccessible(true);
            // 拿到需要反射的方法,注意传参数类型,防止方法重载
            Method method = pathlist.getClass().getDeclaredMethod("makeDexElements", List.class,File.class,List.class,ClassLoader.class);
            // 修改私有权限
            method.setAccessible(true);
            ArrayList<IOException> suppressedExceptions = new ArrayList<IOException>();
            // 获取一个新的数组, 第一个参数为null:因为方法是static的类直接访问,
            Object[] newElements = (Object[]) method.invoke(null,files,optimized,suppressedExceptions,loader);
            // 获取系统的属性
            Object[] systemElements = (Object[]) pathlist.get("dexElements");
            // 通过反射拿到一个新的数组
            Object[] curElements = (Object[]) Array.newInstance(systemElements.getClass().getComponentType(),newElements.length + systemElements.length);
            // 复制数组
            System.arraycopy(newElements,0,curElements,0,newElements.length);
            System.arraycopy(systemElements,0,curElements,newElements.length,systemElements.length);
            // 用新的属性 替换掉 系统的属性
            pathlist.set(systemElements,curElements);
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }

从代码注释中可以很好的理解,整个替换过程,这里是针对 android 28 的所以需要去根据源码适配其他版本,适配很简单只要保证不同版本下面,我们反射拿到的 “属性名” 和 “方法名” 正确就行。

3. 手动生成dex差分包

差分包的生成网上有很多,可以去查看一下几个blog:

推荐:

https://blog.csdn.net/lijia1201900857/article/details/79468986

https://www.jianshu.com/p/2cc4f5665cbb

https://blog.51cto.com/13952501/2169865

知识点介绍的非常完整,可以去参考,对与dex差分包的生成 大同小异,流程都一样。

参考学习:安卓App热补丁动态修复技术介绍

GitHub : https://github.com/WangRain1/HotFix

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

WangRain1

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值