shorebird热更新
Shorebird 官方的检查接口和补丁文件都在境外。手机在国内做一次更新,要先请求 api.shorebird.dev 拿到补丁号、下载地址和 hash,再把 vmcode 拉回来。这条链路延迟高,连接经常被重置:检查超时,下载停在一半。补丁对哈希极敏感,文件不完整就装不上,表现为能打开应用、但热更一直失败。
手机侧的检查和下载因此改走国内服务器。开发机执行 shorebird release、shorebird patch,以及从官方拉取 vmcode,仍然访问 Shorebird 官方。国内服务器只按已发布的版本把补丁交给手机,不代替官方做发布。
实现逻辑
shorebird.yaml 打进安装包,原生更新器启动时读这份资源。base_url 指向国内服务,auto_update: false,由页面按钮调用 shorebird_code_push。给旧包打 patch 改不了检查地址,地址变更必须发新的整包。
1 | app_id: <shorebird.yaml 中的 app_id> |
base_url 不加引号,末尾不加斜杠。更新器会自己拼出:
https://<域名>/api/shorebird/api/v1/patches/check
Nginx 去掉 /api 前缀后,Express 收到 /shorebird/api/v1/patches/check。服务在 Applets_admin/server,路由 routes/shorebird.js,挂在 /shorebird。check 与 events 各挂两条路径,处理函数相同。更新器实际打的是带 /api/v1 的那条,/patches/check 留给手工探测。补丁文件由同一路由下的静态目录直接下发。
手机里的更新器只做三件事:
check:按app_id、channel、release_version、platform、arch查patch_info.json。有更新时返回补丁号、download_url、hash。没有匹配记录、补丁号为 0,或手机已经装着同一个补丁号时,返回patch_available: false,页面显示「已是最新」。- 按
download_url下载vmcode,用响应里的hash校验。hash必须是官方 check 接口原样返回的字符串。Shorebird 用的不是对文件直接做 SHA256,本地重算会对不上,补丁会被丢掉。 events:安装结束后上报结果。服务只写日志并返回成功,不参与下载和校验。事件失败只影响统计。检查返回 200 且哈希一致时,补丁仍会装上。
patch_info.json 放在 /opt/shorebird/server/patch_info.json,只认顶层 releases 数组。一条记录同时绑死 release 版本、平台和架构。改完保存即生效,不用重启。补丁文件放在 /opt/shorebird/patches/。
1 | { |
目录和文件名里不要使用 +。版本 1.1.0+6 在路径里写成 1.1.0-6。JSON 里的 release_version 仍写 1.1.0+6,必须和安装包版本一致。
开发机上的 Shorebird CLI 也会读取 shorebird.yaml 的 base_url。不另外指定时,shorebird release 和 shorebird patch 会去请求国内服务器。管理接口不存在,从而报「找不到 app」。手机不读这个环境变量。PowerShell 里每次新开窗口后先执行:
1 | $env:SHOREBIRD_HOSTED_URL = "https://api.shorebird.dev" |
页面用 ShorebirdUpdater 手动检查和下载:
1 | final ShorebirdUpdater updater = ShorebirdUpdater(); |
| 状态 | 页面 |
|---|---|
upToDate |
已是最新 |
outdated |
有可下载的补丁 |
restartRequired |
补丁已下载,完全退出后重新打开才会生效 |
unavailable |
当前安装包没有 Shorebird 更新器 |
flutter build apk 和 flutter run 的包没有 Shorebird 引擎,页面会显示「更新器不可用」。只有 shorebird release 打出的整包能收补丁。
base_url、版本号、原生代码、Manifest、Gradle、带原生实现的插件,都不能靠补丁下发。这些变更要先升高 pubspec.yaml 的 version,再执行 shorebird release,用户重装。只改 Dart 时版本号不动,执行 shorebird patch。
Flutter 3.38.1 的检查请求经常不带当前补丁号,服务按 0 处理,会反复返回同一份补丁。手机已经装过同一个号时会自己跳过下载。日志里 current 仍为 0 时,以页面补丁号为准。
给当前版本发补丁
只改 Dart,版本保持 1.1.0+6。窗口里先设置 SHOREBIRD_HOSTED_URL。
本机 flutter --version 往往不是这条 release 的引擎。1.1.0+6 用的是 Flutter 3.38.1,patch 会按 release 记录自动使用它。CLI 的 patch 没有 --flutter-version 参数,带上会失败。
1 | shorebird patch --platforms=android --release-version=1.1.0+6 |
发布成功后马上向官方查询。download_url 有时效,查到后立刻下载。不要覆盖已经在用的 patch-1.vmcode、patch-2.vmcode。下面以 3 号补丁为例,下次把编号改掉即可。
1 | curl.exe -X POST "https://api.shorebird.dev/api/v1/patches/check" -H "Content-Type: application/json" -d "{\"app_id\":\"<app_id>\",\"channel\":\"stable\",\"release_version\":\"1.1.0+6\",\"platform\":\"android\",\"arch\":\"aarch64\"}" |
上传到 /opt/shorebird/patches/1.1.0-6/aarch64/patch-3.vmcode。同一条 Android arm64 记录改三个字段:latest_patch_number、download_url、hash。hash 整段复制官方返回值。
确认文件可以下载,大小与本地一致:
1 | curl.exe -I "https://<域名>/api/shorebird/patches/1.1.0-6/aarch64/patch-3.vmcode" |
手机关掉代理。点「检查更新」,应显示「有可下载的补丁」。点「下载补丁」,出现「补丁已下载」后完全杀掉应用再打开。当前补丁号变为这次的编号。
发新的安装包
先把 pubspec.yaml 的 version 升高,例如 1.1.0+7。shorebird.yaml 保持原来的 app_id、base_url、auto_update: false。Flutter 仍用 3.38.1,换引擎时再改 --flutter-version。
1 | $env:SHOREBIRD_HOSTED_URL = "https://api.shorebird.dev" |
安装前把 apk 当 zip 打开,确认 assets/flutter_assets/shorebird.yaml 含有未加引号的国内 base_url。
新版本的补丁号从 1 重新计算。patch_info.json 里另起一条 release_version,不要把旧版本的 vmcode 填给新版本。旧版本那条留着,直到不再维护该安装包。
已经在 Shorebird 上发布过的版本,不要再对同一版本执行 shorebird release。要重装已有整包时用:
1 | shorebird releases get-apks --release-version=1.1.0+6 |
Windows 上检出 Flutter 3.38.1 会遇到路径过长。先打开 Git long paths,否则 patch 会报 Filename too long:
1 | git config --global core.longpaths true |
回滚
作废某个补丁时,把该编号写入对应记录的 rolled_back_patch_numbers。
退回更早的一份补丁:latest_patch_number、download_url、hash 改成那一份,响应里仍然带上这个 patch。手机一旦运行过更高编号,本地更旧的补丁文件会被引擎删掉。只返回「没有更新」会回到原包,所以退回旧补丁时必须再次下发那一份文件。
退回原包:latest_patch_number 设为 0,响应里不带 patch,只带作废编号。
正在运行的进程要杀掉后才会换代码。再冷启动一次才运行退回后的结果。
iOS
iOS 使用同一份 shorebird.yaml 和同一台国内服务器。Android 的 vmcode 不能装到 iPhone 上。真机检查参数是 platform: ios、arch: aarch64。patch_info.json 里追加一条,原来的 Android 记录留着。
在 Mac 上操作,不要再执行 shorebird init,否则会换成新的 app_id。
1 | flutter create --platforms=ios . |
补丁文件放到 /opt/shorebird/patches/1.1.0-6/ios-aarch64/patch-1.vmcode。hash 用这次官方返回的值。iPhone 使用普通网络,点「检查更新」,再点「下载补丁」,完全杀掉 App 后重新打开。




