Sfpatcher
stable & fast to patch apk archives, used by Android app store. 为安卓应用商店使用而优化的apk增量更新算法。
Install / Use
/learn @sisong/SfpatcherREADME
sfpatcher:针对应用商店的apk增量算法
v1.0.15 已正式上线,为多家大厂和数亿手机终端用户提供更新服务,当前最新版本 v1.4.0
sfpatcher 命令行工具下载(支持Windows、Linux、MacOS),
命令行使用说明
需要商业授权(含源代码&培训),请联系作者: housisong@hotmail.com
定义
app:本文档一般指智能手机和Pad设备上的软件,包括应用和游戏等。
应用商店:这里指当前安卓智能设备上负责管理app的软件,支持app的下载、更新、推荐等功能;软件也可能分成应用市场和游戏中心两个独立应用。
apk:安卓设备上的约定的软件包格式,它属于特殊约定格式的zip压缩档案格式的一种。为了防止篡改,发布的apk都带有签名。
diff:这里指一种创建补丁的算法,它能计算出两个数据之间的差异,比如生成从旧版本数据到新版本之间的补丁数据。
patch:打补丁,diff算法的逆算法,它能在旧版本数据基础上应用补丁来得到新版本的数据。
背景
随着智能手机的普及和应用数量的爆发式增长,手机上大量app的下载和更新将产生巨大的数据流量。手机上的应用商店一般都会部署增量更新系统来节省大量的服务带宽成本和提升用户体验(节省用户流量和减少下载时间),而不用每次都下载完整版本。
应用商店使用的增量更新算法一般选择使用的是基于字节的 diff 和 patch 算法,包括:BsDiff 、xdelta3、HDiffPatch 等。
- BsDiff 是当前选择使用较多的算法,创建的补丁小,代码量小,容易移植。但diff和patch场景下执行速度都很慢、内存占用巨大,应用于应用商店等场景时需要进行一些修改定制。
- xdelta3 在diff和patch场景下执行速度都很快,输出标准化的补丁格式(vcdiff格式,也有改成gdiff的),apk补丁大小和BsDiff接近或略大,内存占用中等。该算法在diff处理较大文件的时候(比如几百MB以上),常会输出不正常的巨大补丁,除非使用和文件大小相当的参考内存来diff和patch,而这时patch时内存占用可控的优点就没有了。
- HDiffPatch 是作者开源的算法,创建的补丁一般比BsDiff略小一些,diff(-s模式)和patch场景下执行速度都很快,内存占用可控并很小。 diff时也支持-m模式,用更大的内存和时间代价(这时也比BsDiff快很多倍)来得到更小一些的补丁包。 HDiffPatch 也可以部分兼容 BsDiff4、xdelta3 和 open-vcdiff 的补丁。 HDiffPatch 现在已经被vivo、小米、荣耀、OPPO、华为、腾讯、字节跳动、传音、米哈游等所使用。 从为4KB RAM内存、8位CPU的MCU设备创建OTA增量补丁(4KB内存里一边解压一边patch!),到为上百GB的游戏创建更新补丁,HDiffPatch 算法得到了越来越广泛的应用。
针对apk的diff&patch算法
前面提到的增量更新实现方案都只是把apk单纯的看作文件数据直接用算法进行diff&patch,而没有考虑apk包本身是一个zip压缩包的事实。这种简单使用方式可以概括为公式:
diffFile:= diff(oldApk,newApk)
newApk := patch(oldApk,diffFile)
大家可以试试:一个大文件在1/4处简单进行修改,如果把修改前后的2个文件分别压缩成zip文件,用二进制工具打开2个zip文件对比会发现:前面一部分的编码相同(那这部分约占1/4),但后面部分的编码数据很可能会完全不同。
压缩算法破坏了修改“现场”,diff算法的优势无法真正发挥出来(生成了巨大的补丁)。那么我们是否可以开发针对apk文件格式的diff&patch算法呢? 它能够识别出上面的情况:其实只是修改了一个字符!(即生成极小的补丁)
很容易想到的一个优化思路,先解压再diff不就好了!原理可以概括为公式:
diffFile:=diff(decompress(oldApk),decompress(newApk))
newApk :=recompress(patch(decompress(oldApk),diffFile))
公式里面的diff和patch函数很好办,选择上面提到的一种基于字节的diff&patch算法就行;decompress函数的实现也较简单,就是zip包的解压缩算法;recompress函数的实现比较麻烦一些,需要保证精确的原样还原出newApk(避免破坏签名和运行等)。
而 archive-patcher 和 ApkDiffPatch 正是这种思路的实现方案,也包括本文章要介绍的 sfpatcher 方案都基于这个思路。
- archive-patcher 谷歌开源的一个针对apk文件的diff&patch实现,在谷歌play商店中使用。内部使用了BsDiff算法作为基础,主要用java语言开发,针对解压状态总数据量小于512MB的并且使用了zlib的deflate压缩算法创建的apk文件,执行优化的补丁算法。 该方案在patch时比较慢,特别是部分略大的apk文件使用了较高的压缩级别时,这时重新压缩出新版本apk会慢的让用户无法接受,所以该优化方案一般用在可以后台更新的场景下。
- ApkDiffPatch 是作者开源的算法,为给自己团队开发的apk更新来开发的方案,创建的补丁平均比 archive-patcher 小很多,patch速度中等;方案一般用在可以自己对需要升级的apk进行重新签名的场景,而不能部署于应用商店。内部使用了 HDiffPatch 算法作为基础,用C\C++语言开发。为了patch时精确还原,和加快压缩还原时的速度,需要对发布的apk文件执行 ApkNormalized 预处理流程(使用了较快的压缩参数,处理过的apk需要重新签名)。 patch时可以支持并行压缩来加快apk还原速度,但这时内存资源占用比较严重,和多个线程正在分别压缩的多个文件源大小和其压缩后大小的总和相当。
sfpatcher 是什么?
针对压缩档案文件的高性能增量更新方案。类似于 archive-patcher 方案可部署于应用商店的diff&patch算法,该领域的重要技术进展。
内部使用了 HDiffPatch 算法作为基础,用C\C++语言开发,当前支持为deflate格式压缩的数据创建优化的补丁(用zlib压缩的支持最好),支持在多种档案格式文件之间创建优化的补丁。
sfpatcher 之道
- 针对应用商店的场景专门设计,优化补丁大小,支持大型游戏,patch时精确快速还原任意apk文件,能够用于用户交互场景。
- 多级可选的补丁包大小,极致的patch速度:提供比谷歌archive-patcher方案的补丁(+lzma2压缩后)小2%的情况下,patch速度是其30倍!补丁比其小24%的情况下,速度是其8倍(这时补丁比BsDiff方案小约55%并且速度快得多)! (注1)
- patch时的内存等资源占用可控;diff时支持多种方案限制patch时的最大内存占用到合适的约定水平。即支持patch时O(1)平均内存占用的优化补丁包,并且patch过程中不使用临时文件!
- 优化的用户体验:因为补丁变小,下载时间也会变短,从而可能缩短整体更新时间,也更省电省流量。还可以支持下载数据的同时就开始patch,不用将补丁文件保存到内存或硬盘上,结合快速的patch,很多时候下载完成时,就可以得到了完整的新版本apk文件。
- 利用精确还原算法,对于初次下载的apk文件也能进行解压后的重压缩;从而节省用户初次下载apk时的流量。最多情况下平均比直接下载apk文件减少25%的数据,部分文件能减少35%以上的数据!
- 支持从中断的位置继续patch的特性,节省程序被终止后再次执行程序时的打补丁时间。
- 高扩展性,框架支持多种压缩档案格式(apk、zip、zip64、jar、hap、gz、tar等)和其嵌套(如apk中多个子apk); 档案格式本身和档案中数据的压缩算法都只是以插件的形式获得支持。patch端的执行,设计上不依赖于具体的档案格式。
- patch端支持对客户端的oldApk文件进行虚拟化;比如可以用一些简单的描述数据来移除oldApk(v1--v4签名)中添加的各种类型渠道号的影响,提高了补丁适应能力。
- diff端参数可选择性丰富,对各种使用场景可以定制性的设置合适的控制参数。
- patch结果提供丰富的错误号,并进行高性能和高强度的数据完整性校验,以利于追踪patch失败的原因,提高升级成功率。
- 支持专用的标准化流程sf_normalize,在保持还不错的patch合成速度的情况下,进一步降低30%补丁包大小。(注意:该特性需要第三方apk厂商配合,对apk执行标准化处理,并重新签名)
注1:所有测试数据来源于收集的一些常用apk应用,共32个用例;并在ARM CPU Kirin980上测试了部分patch。(见性能测试对比数据)
方案主要特性对比
sfpatcher 和 archive-patcher:
- sfpatcher 的patch端比 archive-patcher 快很多倍(相近补丁大小的情况下速度是其30倍),资源占用小(O(1)),能够满足各种使用场景的要求;而 archive-patcher 还原慢,资源占用大,一般用于后台更新场景。
- sfpatcher 生成的补丁大小在很多情况下也可以比 archive-patcher 的补丁(压缩后)更小。
- sfpatcher 创建压缩后的补丁,执行patch前不需要额外步骤解压缩补丁,边patch边随时解压缩补丁数据;而 archive-patcher 创建的补丁需要额外压缩和patch前解压。
- sfpatcher 在patch时不需要对oldApk提前进行解压,边patch边随时解压用到的oldApk中的数据,可以始终保持O(1)内存占用;而 archive-patcher 需要提前将oldApk中用到的数据全部解压缩到一个临时文件里。
- sfpatcher 支持大型apk文件,包括游戏等的更新升级,patch速度快;而 archive-patcher 有解压+未压缩总数据最大512M的限制,而且patch也很慢,某些情况下甚至patch需要几分钟。
- sfpatcher 基于C\C++,部署兼容性不受系统限制;结合JNI在安卓上使用,兼容安卓4.1到安卓15;而 archive-patcher 基于 java 并和运行时系统环境耦合重,diff和patch时都很可能会遇到环境兼容性问题。
sfpatcher 和 ApkDiffPatch:
- sfpatcher 可用于广泛的场景,而ApkDiffPatch用于能够对apk文件进行重新签名的场景。
- sfpatcher 的patch端比 ApkDiffPatch 更快一些,多线程时内存占用也比较可控;而 ApkDiffPatch 在并行patch的时候内存占用较大。
- sfpatcher 生成的补丁平均比 ApkDiffPatch 稍大一些;当然对于同样ApkNormalized化过的apk,两个方案生成的补丁大小相近,即 sfpatcher 可以平滑的替代 ApkDiffPatch方案。并且,新版本sfpatcher提供了专用的sf_normalize标准化程序,补丁包大小一致,但patch合成速度可以进一步加倍!
- sfpatcher 插件框架可支持多种档案格式,并且档案格式和patch端无关,兼容性和扩展性更好;而 ApkDiffPatch 的patch端和zip、apk格式、签名方式耦合过重,随着apk格式、签名方案和apk工具的变动,容易有兼容性问题(可能造成无法准确还原newApk或者无法优化补丁包大小)。
参与测试的diff&patch方案
测试目的
对比多种diff&patch方案在apk文件增量更新方面的运行数据;
测试项主要包括:diff速度、diff内存占用、补丁大小(用压缩率代替,压缩后补丁大小/新版本apk大小)、patch速度、patch内存占用(后面这3项指标可能更重要一些)
测试用例
收集了32组测试用例;这些apk下载于小米应用商店、谷歌Play商店,按照下载量大和最近进行过更新为标准进行收集。
限于有限的用例和收集偏差,数据和实际情况可能略有差异。 旧版本apk平均大小103.2MB,新版本apk平均大小103.8MB。
| 编号|app|新apk <-- 旧apk|新apk大小|旧apk大小| |----:|:---:|:----|----:|----:| |1|<img src="https://github.com/sisong/sfpatcher/raw/master/img/cn.wps.moffice_eng.png" width="36">|cn.wps.moffice_eng_13.30.0.apk <-- 13.29.0|95904918|94914262| |2|<img src="https://github.com/sisong/sfpatcher/raw/master/img/com.achievo.vipshop.png" width="36">|com.achievo.vipshop_7.80.2.apk <-- 7.79.9|127395632|120237937| |3|<img src="https://github.com/sisong/sfpatcher/raw/master/img/com.adobe.reader.png" width="36">|com.adobe.reader_22.9.0.24118.apk <-- 22.8.1.23587|27351437|27087718| |4|<img src="https://github.com/sisong/sfpatcher/raw/master/img/com.alibaba.android.rimet.png" width="36">|com.alibaba.android.rimet_6.5.50.apk <-- 6.5.45|195314449|193489159| |5|<img src="https://github.com/sisong/sfpatcher/raw/master/img/com.amazon.mShop.android.shopping.png" width="36">|com.amazon.mShop.android.shopping_24.18.2.apk <-- 24.18.0|76328858|76287423| |6|<img src="https://github.com/sisong/sfpatcher/raw/master/img/com.baidu.BaiduMap.png" width="36">|com.baidu.BaiduMap_16.5.0.apk <-- 16.4.5|131382821|132308374| |7|<img src="https://github.com/sisong/sfpatcher/raw/master/img/com.dragon.read.png" width="36">|com.dragon.read_5.5.3.33.apk <-- 5.5.1.32|45112658|43518658| |8|<img src="https://github.com/sisong/sfpatcher/raw/master/img/com.ebay.mobile.png" width="36">|com.ebay.mobile_6.80.0.1.apk <-- 6.79.0.1|61202587|61123285| |9|<img src="https://github.com/sisong/sfpatcher/raw/master/img/com.eg.android.AlipayGphone.png" width="36">|com.eg.android.AlipayGphone_10.3.0.apk <-- 10.2.96|122073135|119046208| |10|<img src="https://github.com/sisong/sfpatcher/raw/master/img/com.google.android.apps.translate.png" width="36">|com.google.android.apps.translate_6.46.0.apk <-- 6.45.0|48892967|48843378| |11|<img src="https://github.com/sisong/sfpatcher/raw/master/img/com.google.android.googlequicksearchbox.png" width="36">|com.google.android.googlequicksearchbox_13.38.11.apk <-- 13.37.10|190539272|189493966| |12|<img src="https://github.com/sisong/sfpatcher/raw/master/img/com.jingdong.app.mall.png" width="36">|com.jingdong.app.mall_11.3.2.apk <-- 11.3.0|101098430|100750191| |13|<img src="https://github.com/sisong/sfpatcher/raw/master/img/com.netease.cloudmusic.png" width="36">|com.netease.cloudmusic_8.8.45.apk <-- 8.8.40|181914846|181909451| |14|<img src="https://github.com/sisong/sfpatcher/raw/master/img/com.reddit.frontpage.png" width="36">|com.reddit.frontpage_2022.36.0.apk <-- 2022.34.0|50205119|47854461| |15|<img src="https://github.com/sisong/sfpatcher/raw/master/img/com.sankuai.meituan.takeoutnew.png" width="36">|com.sankuai.meituan.takeoutnew_7.94.3.apk <-- 7.92.2|74965893|74833926| |16|<img src="https://github.com/sisong/sfpatcher/raw/master/img/com.sankuai.meituan.png" width="36">|com.sankuai.meituan_12.4.207.apk <-- 12.4.205|93613732|93605911| |17|<img src="https://github.com/sisong/sfpatcher/raw/master/img/com.sina.weibo.png" width="36">|com.sina.weibo_12.10.0.apk <-- 12.9.5|156881776|156617913| |18|<img src="https://github.com/sisong/sfpatcher/raw/master/img/com.smile.gifmaker.png" width="36">|com.smile.gifmaker_10.8.40.27845.apk <-- 10.8.30.27728|102403847|101520138| |19|<img src="https://github.com/sisong/sfpatcher/raw/master/img/com.ss.android.article.news.png" width="36">|com.ss.android.article.news_9.0.7.apk <-- 9.0.6|54444003|53947221| |20|<img src="https://github.com/sisong/sfpatcher/raw/master/img/com.ss.android.ugc.aweme.png" width="36">|com.ss.android.ugc.aweme_22.6.0.apk <-- 22.5.0|171683897|171353597| |21|<img src="https://github.com/sisong/sfpatcher/raw/master/img/com.taobao.taobao.png" width="36">|com.taobao.taobao_10.18.10.apk <-- 10.17.0|117218670|117111874| |22|<img src="https://github.com/sisong/sfpatcher/raw/master/img/com.tencen
Related Skills
node-connect
338.0kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
83.4kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
openai-whisper-api
338.0kTranscribe audio via OpenAI Audio Transcriptions API (Whisper).
commit-push-pr
83.4kCommit, push, and open a PR
Security Score
Audited on Feb 24, 2026
