type
status
date
slug
summary
tags
category
password
icon
热更新 / 热修复
不安装新版本的软件,直接从⽹络下载新功能模块来对软件进⾏局部更新
热更新和插件化的区别
区别有两点
- 插件化的内容在原 App 中没有,⽽热更新是原 App 中的内容做了改动
- 插件化在代码中有固定的⼊⼝,⽽热更新则可能改变任何⼀个位置的代码
热更新的原理
- ClassLoader 的 dex ⽂件替换
- 直接修改字节码
前置知识:loadClass() 的类加载过程
- 宏观上:是⼀个带缓存的、从上到下的加载过程(即⽹上所说的「双亲委托机制」)
上图有点错误,不是 loadClass 方法,是 findClass 方法
- 对于具体的⼀个 ClassLoader:
- 先从⾃⼰的缓存中取,取到了,就直接返回。
- ⾃⼰没有缓存,就找⽗ ClassLoader 要(parent.loadClass())
- ⽗ View 也没有,就⾃⼰加载(findClass()),加载成功就添加到缓存并返回。
- BaseDexClassLoader 或者它的⼦类(DexClassLoader、PathClassLoader 等)的 findClass():
- 通过它的 pathList.findClass()
- 它的 pathList.loadClass() 通过 DexPathList 的 dexElements 的 findClass()
- 所以热更新的关键在于,把补丁 dex ⽂件加载放进⼀个 Element,并且插⼊到 dexElements 这个数组的前⾯(插⼊到后⾯的话会被忽略掉)
⼿写热更新
- 因为⽆法在更新之前就指定要更新谁;所以不能定义新的 ClassLoader,⽽只能选择对 ClassLoader 进⾏修改,让它能够加载补丁⾥⾯的类
- 因为补丁的类在原先的 App 中已经存在,所以应该把补丁的 Element 对象插⼊到 dexElements 的 前⾯才⾏,插⼊到后⾯会被忽略掉。
- 具体的做法:反射
- ⾃⼰⽤补丁创建⼀个 PathClassLoader
- 把补丁 PathClassLoader ⾥⾯的 elements 替换到旧的⾥⾯去
- 注意:
- 尽早加载热更新(通⽤⼿段是把加载过程放在 Application.attachBaseContext())
- 热更新下载完成后在需要时先杀死程序才能让补丁⽣效
- 优化:热更新没必要把所有内容都打过来,只要把改变的类拿过来就⾏了
- ⽤ d8 把指定的 class 打包进 dex
- 完整化:从⽹上加载
- 再优化:把打包过程写⼀个 Task
- 作者:shuouyang
- 链接:https://notion-tree.vercel.app/article/11160301-de50-421e-9c84-afedc6ec5839
- 声明:本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。