什么是增量更新
简单的说, 就是用户本地有个文件X, 当前版本是A, 大小100M. 现在这个文件需要升级到版本B, 大小110m.
用户可以直接下载版本B, 覆盖本地的版本A, 完成升级. 这是普通升级. 使用流量110M.
用户还可以只下载版本B和版本A之间的差异包, 然后在本地将差异包和版本A结合, 生成版本B. 完成升级. 使用流量可能只有10M左右. 这就是"增量更新"了
Archiv-Patcher
实现增量更新的方案很多, 经过初步了解和比较, 我们选择google的 Archive-Pather
选择它的原因:
- 业务层逻辑是纯java的. 容易接入Android.
- 在开源的代码中, 核心部分也有java实现. 容易修改
- 压缩核心使用zlib. 虽然是native的, 在各个系统中都有稳定实现.
- 不需要重新签名, 用户侧可以使用
问题
因为是纯java的代码, 所以接入和应用都非常简单. 但是实际还是遇到了一些问题.
zlib版本更新.
实验发现安卓11上合并操作是必然失败的. 设备上合并的结果和实际目标大相径庭.
翻阅源码的issues, 发现已经有人提了这个问题, 但是google方面似乎并没想解决. 有人提到是因为zlib的版本不同导致.
回头再重新翻看源码的ReadMe. 确实提到了zlib版本等环境问题会导致合并结果异常, 并且提供了一个"适配窗口"来做环境检查. 能通过检测说明可以使用, 否则不能. 果然在Android 11以上的设备里, 检测没有通过.
定位到了问题, 那就解决它. 之前调研的时候也说了, 选择Archive-pather是因为它好改好移植, 与Android兼容性强. 我们找到一个可用的zlib版本. 将他内嵌到app中编译成so库. 另外因为zlib是通过java.util.zip包引入的, 所以java.util.zip的包也要移植到app内, 以保证使用的自家的zlib.
有专业的jni大佬支持, 整个移植过程还比较顺利. 因为使用的是自家移植的zlib. 所以不再有Android版本适配的问题, 都可以正常通过适配窗口的测试.
合并异常
解决了zlib版本问题, 发现还是有合并异常. 这些异常都发生在正常合并结束后, md5校验失败. 说明合并流程是正常执行完毕的, 但是合并的结果有问题.
这就需要分析Archive-patcher的合并流程了. 除了分析出流程外, 还需要在合并中插入关键日志, 以帮助我们分析用户合并失败的原因. 好在源码ReadMe中比较详细的介绍了patcher文件的格式. 结合源码, 慢慢啃到一些标记数据. 需要进一步分析过程, 试验验证.
题外
写文章的时候, 重新翻看issues, 发现有推荐sfpatcher. 当时遇到这个问题的时候还没有这个评论. 是一个比Archive-patcher更高效稳定的增量方案. 只发布了编译工具和文档. 需要商业授权. 从文档看, 确实提供了很多高效的思路:
- 以一种类似流的方式生成和使用补丁. 指定缓存的大小, 对比流内容, 并更新到目标文件
- archive-patcher非常消耗硬盘空间. 是可以重点优化的点.
- 核心算法可以替代. java/c/c++都可以
- 可以商业化, 属实意外.