Android热修复方案研究

一、概述

  以前对Android 的热修复方案有一些了解,知道几个有名的开源方案,原理大概理解,但是没有整理汇总一下,上周听了玉斌大哥在公司做的分享后,感受颇多觉得写篇博客记一下,不能浪费。

  热修复是指在不发新版的情况下修复线上的紧急 bug,长久以来做移动开发的人员都羡慕做后端或者做 web 前端的人员可以随时发布来修复 bug。那么 移动开发有没有这样的方案呢?

Hybrid

如:PhoneGap、国内的 AppCan。本质还是利用了 Web 开发在更新上的优势,但体验不如 Native。

ReactNative

像Web一样可以随时发布,又有原生的体验,所以这个方案优势挺大,有很多项目都在尝试。

Native HotFix

可以不用发新版本,直接下发补丁包修复 bug,纯原生体验。

iOS有JSPatch、Android 有 AndFix、Nuwa、DroidFix 、HotFix等

二、Android Native HotFix

下面按照原理分类为

(一) Dex分包方案(QZone的方案)(如 Nuwa、DroidFix、HotFix、RocooFix)

  原理:如果多个 dex 里有相同的class,那么虚拟机会优先使用最先加载的 dex 中的 class。所以将有问题的类打包到一个新dex(pathc.dex),然后下发给 app,app 启动时将该patch.dex插入到其他 dex 前面优先加载。就达到了 替换有问题 class 的目的。

这里有两个难点:

1. 改变dex加载顺序

2. class_ispreverified

错误信息:java.lang.IllegalAccessError: Class ref in pre-verified class resolved to unexpected implementation

原因

  dex转化成odex时会执行dvmVerifyClass进行类的校验,如 B的引用和B都在一个 dex 里,则 B 会被打上class_ispreverified标记,但是我们优先加载 A’,A’和B在两个不同的 dex,但 B 却打上了class_ispreverified标识所以就报错了。

  两个相关联的类在不同的dex中并且有class_ispreverified就会报错,谷歌MultiDex拆分dex的时候就会处理这种情况如果A 和 B 不在同一个 Dex 就不会给 B标记class_ispreverified。

  但我们是热修复,patch.dex 是事后打的 dex,class.dex已经安装的用户手机里了,所以我们无法修改 B 的标识。

解决方案:

我们发布版本打包时,将所有的类都引用一个单独dex的一个类,这个所有的类都不会标记class_ispreverified了,当然这也就失去了class_ispreverified的意义,会有性能上的影响。

更新:还有一个巧妙的方案来防止class_ispreverified,就是在工程里放入一个android.jar 已经存在的类,这样会导致multiple class declarations 从而不会再对class 校验。比如使用com.android.internal.util.Predicate,这样类的特点是特别简单没有额外引用,然后它从android api 8 就存在了。

QZone 安卓App热补丁动态修复技术介绍

深度理解Android InstantRun原理以及源码分析

(二)smali diff方案(阿里的方案)(以 AndFix为例)

1. 原理

2. 方法替换(AOP)

  搞过 Android Aop 的同学知道,我们可以在执行一个方法的前插入另一个方法,运用这个思路,我们可以把有 bug 的方法替换成我们下发的新方法。

下边是 AndFix 使用 JNI hook 方法的一段代码,供大家理解。另一个实现AOP 的开源方案

dexposed

Android AOP 常用有的方法有 JNI HOOK 和 静态织入。

常用的静态织入的库有:

1、APT 是代码生成,java文件 生成 java文件。使用的annotation类型是SOURCE.

2、Aspectj是静态织入。静态织入:指在编译时期就织入,即:编译出来的class文件,字节码就已经被织入了。AspectJ就是一个编译器,把java文件编译成想要的class文件。使用的annotation类型是SOURCE.

3、ASM或者Javassist或者dexmaker 是Java 字节码操控框架。它能被用来动态生成类或者增强既有类的功能。使用的annotation类型是class.

注:

cglib底层采用ASM字节码生成框架,实现动态代理实现AOP

cglib在android中是不能使用的。使用dexmaker框架来仿照动态生成.dex文件,实现cglib的动态代理功能

3. ApkPatch tool

  AndFix 提供了一个打包 patch 的工具,这个工具将旧Apk和新Apk做 diff 得出一个patch apk。

熟悉增量更新的同学应该知道,增量更新也是做 diff 求增量包,但是他是在字节码层面上求增量包,增量包+旧 apk 合并成新 apk。

  而ApkPatch是通过对比两个 apk 反编译后的 smali 文本文件来求 diff 的。所以他是从 class 层面上求 diff。得到的patch就是标准的 dex 包。 该 dex 可直接被 load。

4. 以前注意事项

一个 patch 包要修复哪些类的哪些方法,这个路径信息会被打包到 patch.apk 的META-INFO里

patch.apk在打包时要使用相同的签名。

混淆的情况:打包 patch.apk 时要使用主apk混淆时导出的 mapping 文件,防止主apk和patch.apk 由于混淆而无法对应相应的类和函数。

目前 apkpatch 没有开源,还有一些小 bug,如不支持 mulitdex。虽然没有开源但还是有方法修复的,比如反编译导出源码、还有 javasist 神器。具体怎么修复以后再说,也可能官方会修复。

(三)微信的方案

AndFix 由于影响正向开发过程(只能修改方法、不能修改 field、不能新增类等问题)、库本身难于维护以及发现的莫名其妙的 bug(不同rom对dex的校验机制不一致)。

nuwa 仅支持更新 Java 代码,不能更新资源和 so 文件。

全量替换新的Dex。即我们完全使用了新的Dex,那样既不出现Art地址错乱的问题,在Dalvik也无须插桩。当然考虑到补丁包的体积,我们不能直接将新的Dex放在里面。但我们可以将新旧两个Dex的差异放到补丁包中,最简单我们可以采用BsDiff算法。

附《Android核心知识笔记2020》分享

前段时间我和圈子里的几位架构师朋友一起闲聊时的突发奇想,我们在学习Android开发的时候或多或少也受到了一些前辈的指导,所以想把这份情怀延续下去。三个月后,这套资料就出来了,需要这份资料的朋友加Android学习交流群1049273031即可获取。

你可能感兴趣的:(Android热修复方案研究)