tinker热修复之类修复

Tinker热修复包含多种类型的修复,如类修复,资源修复等,此处介绍类修复的基本原理。

修复原理

应用类的代码均位于apk包的dex文件中,在安卓设备上安装后dex文件一般位于/data/app/packageName-1/下的目录中,类的加载则需要依靠ClassLoader,由于有多个dex文件,加载的规则为越靠前的dex优先被系统使用。

常见的PathClassLoader继承自BaseDexClassLoader,源码地址/libcore/dakvik/src/main/java/dalvik/system/BaseDexClassLoader,该类只有一个属性

private final DexPathList pathList;

该类提供findClass方法在pathList中找期望的类,而dexElements则位于DexPathList类中。

类修复的原理是hook了ClassLoader.pathList.dexElements[],因为ClassLoader的findClass是通过dexElements中的dex来查找类

修复后dexElements如下

patch.dex - classes.dex - classes1.dex - classes2.dex

类修复的具体实现

在应用编译生成dex文件时,需要确保主包中代码没有问题,热修复相关的代码也应该在主包中完成。在模块build.gradle文件中的配置如下

android {
    defaultConfig {
        ......
        // 开启分包
        multiDexEnabled true
        // 配置分包细节
        multiDexKeepFile file('multidex-cofig.txt')
    }
    dexOptions {
        javaMaxHeapSize "4g"
        preDexLibraries = false
        additionalParameters = [
            '--multi-dex',    // 多dex分包
            '--set-max-ids-number=50000',    // 每个dex文件包含的方法数上限
            '--main-dex-list=' + '/multidex-config.txt',    // 打包到主包classes.dex的类列表
            '--minimal-main-dex'
        ]
    }
}

dependencies {
    ......
    implementation 'com.android.support:multidex:1.0.3'
}

如上配置显示分包配置文件与build.gradle位于同一目录,该文件配置主包中包含的类,例如

com/example/sample/BaseApplication.class
com/example/sample/BaseActivity.class
com/example/sample/MainActivity.class

在客户端收到用于修复的patch.dex文件后,需要做如下事情

  1. 将patch.dex文件复制到context.getDir(“odex”, Context.MODE_PRIVATE).getAbsoluteDir()中
  2. 自定义一个DexClassLoader,继承自BaseDexClassLoader
  3. 用DexClassLoader加载包含修复代码的patch.dex文件
  4. 将自有的和系统的dexElements数组合并,并且设置patch的element靠前
  5. 通过反射将合并后的dexElements设置给系统的dexElements

其中获取补丁包的简单方式就是修复代码后再打包apk,然后解压apk包拿到其中的dex文件。修复的伪代码如下

// 伪代码
void hotfix(Context context, File dex) {
    // 复制patch.dex到odex目录
    FileUtils.copy(dex.getAbsolutePath(), context.getDir(“odex”, Context.MODE_PRIVATE).getAbsoluteDir() + "/patch.dex";
    // 获取系统ClassLoader和自定义的ClassLoader
    Object systemClassLoader = context.getClassLoader();
    Object myClassLoader = new DexClassLoader(dex.getAbsolutePath(), optmizeDir, null, context.getClassLoader());
    // 获取对应的dexElments然后合并
    Object systemElements = ReflectUtils.getDexElements(ReflectUtils.getPathList(systemClassLoader));
    Object myElements = ReflectUtils.getDexElements(ReflectUtils.getPathList(myClassLoader));
    Object dexElements = ArrayUtils.combine(systemElements, myElements);
    // 将系统ClassLoader的PathList中的dexElements设置为合并后的dexElements
    ReflectUtils.setField(systemDexElementsField, dexElements);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值