QuickPatch 项目地址:
https://gitee.com/egg90/QuickPatch 和 https://github.com/eggfly/QuickPatch 同步更新
类似于美团的 Robust 插桩热修复, 但是代码可读性比较强, 还在继续完善, todo list 在项目 README 里
QuickPatch 项目介绍
年轻人的第一个 Android 插桩热修复框架
基于函数插桩, 兼容性好 (Android 版本升级不需要做修改), 支持热更新无需重启 app, 参考了美团的 Robust 插桩热修复框架, 精简了很多实现细节, 代码可读性高
一句话原理
简单地讲, 就是通过编译时在每个函数的头部插入一个 if 判断和一个 proxy 代理, 就可以在运行时动态替换实现, 无需重启. 代码如下:
- protected void onCreate(Bundle savedInstanceState) {
- if (_QPatchStub != null) {
- // _QPatchStub.proxy() will check method existance and call it
- MethodProxyResult proxyResult = _QPatchStub.proxy(this, "onCreate", "(Landroid/os/Bundle;)V", new Object[]{savedInstanceState});
- if (proxyResult.isPatched) {
- return;
- }
- }
- // origin implementation below
- super.onCreate(savedInstanceState);
- // ...
- }
设计思路
QuickPatch 和美团 Robust 的区别是, Robust 的编译和 dex 阶段分别使用 ASM 和 Smali 做了处理, QuickPatch 仅在 gradle 编译 java 到 class 阶段使用 Javassist 处理, 逻辑简单
不支持自动生成 dex 补丁 (复杂度高, 代码可读性差), 所以需要手动生成补丁, 但是提供了补丁类模版, 写起来很方便
对于 super 的处理使用 native 调用 CallNonVirtual##TYPE##Method() 系列方法实现
计划支持构造函数和增加成员函数的热修复
可能计划支持非 Android 的纯 Java 代码的热修复
DEMO
使用说明
打开 app/build.gradle 中的一行 apply plugin: 'quickpatch.gradleplugin'
然后使用 AndroidStudio 或者./gradlew 执行下面任务:
- ./gradlew gradleplugin:uploadArchives # 编译插桩插件
- ./gradlew app:installDebug # 使用插件编译 app 代码并插桩
为了方便点击 app 内的 Enable Patch 按钮可以模拟补丁加载效果, 实际上是使用原本的 ClassLoader 加载了 apk 内打包好的的 QPatch 类
同包名下后缀是_QPatch 的类是补丁类, 如 MainActivity 类的补丁类对应名字是 MainActivity_QPatch
接下来框架代码会使用一个新的 ClassLoader 加载 dex, 然后反射识别并查找相应的函数是否存在, 如果存在则新的函数里面的逻辑会被调用
补丁文件名一般是 patch.dex, 生成 dex 需要手动使用命令, 比如 dx --dex --output=patch.dex MainActivity_QPatch.class
补丁文件需要手动放置到 sd 卡下, 比如 adb push patch.dex /sdcard/
然后点击 app 内的 Enable Patch 按钮即可实时加载补丁, 看到 pid 不会有变化
性能优化思路
减少没有 patch 的时候所有函数调用损耗
减少有 patch 时, 但没有走到 patch 涉及到的类时的损耗
减少有 patch 时, 走到 patch 类, 但是没走到 patch 函数时的损耗
减少有 patch 时, 走到 patch 类, 并走到 patch 函数时的损耗
优化 patch 函数内不包含反射, 和包含 native 反射或 java 反射的这三种情况
来源: http://blog.51cto.com/13941246/2164761