Skip to content

版本闸门与软提示

服务端通过三种机制控制客户端版本:两道硬闸门(强制更新)和一道软提示(可暂不更新)。本页讲清三者的语义、优先级与渠道策略。

三种机制对比

机制字段行为优先级
强制更新force_update:true + update_url_*版本被拒,必须更新或退出高(握手中先判)
版本白名单client.allowed_versions当前版本不在列表 → 强制更新
更新软提示client.latest_version按渠道提示,可"暂不更新"继续低(软)
mermaid
flowchart TB
    REQ["/client/init"] --> H1{版本在<br/>allowed_versions?}
    H1 -->|否| FORCE["200 force_update:true<br/>必须更新(硬闸门)"]
    H1 -->|是| SIGNED["签发会话<br/>下发完整响应"]
    SIGNED --> SOFT{latest_version<br/>按渠道比较高于当前?}
    SOFT -->|是| TIP["客户端弹软提示<br/>可暂不更新"]
    SOFT -->|否| SILENT["不提示,正常进入"]

    style FORCE fill:#ff6b6b,color:#fff
    style TIP fill:#ffd43b
    style SILENT fill:#51cf66

先硬后软:握手时先判版本白名单(硬),通过了才轮到 latest_version(软)。硬闸门不过直接强制更新,根本到不了软提示。

硬闸门:版本白名单

allowed_versions 不为空且当前版本不在列表时,握手返回:

json
{
  "success": false,
  "force_update": true,
  "current_version": "3.9.0",
  "update_url_normal": "https://.../normal.apk",
  "update_url_internal_test": "https://.../internal.apk"
}

这是 HTTP 200,不是 4xx

客户端 Net.postJson 对 HTTP ≥400 会抛 IOException,读不到 body 里的 update URL。所以版本闸门必须用 200 + 顶层 force_update。此分支不签发 access_token,客户端拿到 force_update 就停下走更新流程,不会继续握手。

update_url_* 没配的渠道,对应 key 省略(避免客户端 optString 拿到空串误判)。

软提示:latest_version

这是给"有新版但不强制"场景用的。服务端只需告诉客户端最新版本号是多少,过滤逻辑全在客户端:

mermaid
flowchart TB
    LV["服务端下发 latest_version"] --> CH{用户渠道?}
    CH -->|正式版 normal| N["比较 major.minor<br/>4.0.x → 4.0.y 不提示<br/>4.0 → 4.1 才提示"]
    CH -->|内测版 internal-test| I["比较完整版本<br/>4.0.0 → 4.0.1 也提示"]
    N --> P["提示:立即更新 / 暂不更新"]
    I --> P
渠道何时提示
正式版(normal)仅当 latest_versionmajor.minor 高于当前。补丁号变化(4.0.0→4.0.5)不打扰
内测版(internal-test)任意版本位升高即提示(含补丁号,4.0.0→4.0.1)

软提示可"暂不更新"继续启动,不阻断latest_version 为空/省略则完全不提示。

为什么单一版本号通常就够

因为过滤在客户端,服务端不需要为两个渠道维护两套版本号。举例:服务端对所有人下发 latest_version = "4.0.5":

mermaid
flowchart LR
    LV["latest_version = 4.0.5<br/>(对所有人一样)"] --> A["内测用户(当前4.0.0)<br/>4.0.0 < 4.0.5 → 提示<br/>下内测 APK"]
    LV --> B["正式用户(当前4.0.0)<br/>major.minor 4.0==4.0 → 不打扰"]
  • 内测用户:4.0.0 < 4.0.5 → 提示,下 update_url_internal_test
  • 正式用户:major.minor 4.0 == 4.0 → 不提示,补丁版本不打扰。

之后发 4.1.0,改 latest_version 即可:内测继续提示,正式用户因 4.0 < 4.1 也开始提示,下 update_url_normal。"内测发补丁、大版本才推正式"的策略,靠客户端按位过滤天然实现。

需要两渠道不同版本线(如内测 4.2.x、正式停 4.1.x)时,才按 channel 分别返回不同 latest_version

APK 完整性校验

update_apk_sha256 让客户端安装前校验下载的 APK,防中间人推恶意包:

mermaid
flowchart LR
    D["按渠道下载 APK"] --> H["计算 SHA-256"]
    H --> C{与下发的<br/>update_apk_sha256<br/>一致?}
    C -->|是| INSTALL["✅ 拉起安装"]
    C -->|否| REJECT["❌ 拒装(疑似被篡改)"]

两渠道 APK 不同时按渠道下发哈希

若正式版与内测版是不同的 APK 文件,单一 update_apk_sha256 只能匹配其中一个,另一渠道会校验失败。解法是服务端按请求里的 channel 返回对应渠道那个 APK 的哈希(update_apk_sha256_internal_test 优先用于内测渠道,否则回退通用值)。这正是本仓库已实现的逻辑。

强烈建议下发哈希。缺失时客户端记 WARN 并跳过校验(安全性下降,不建议长期如此)。

软提示与硬闸门的关系

latest_version软提示,不替代强制更新和版本白名单两道硬闸门。三者可同时配置:

  • 硬闸门保证"太旧的版本根本不让玩"。
  • 软提示负责"有新版了,温和提醒一下"。

硬闸门的下载 URL 渠道选择,也跟随用户所选渠道,与软提示一致。

配置入口

全在后台「版本管理」页:

  • 允许的客户端版本(白名单)
  • 最新版本号(软提示)
  • 正式/内测通道 APK URL
  • 各渠道 APK SHA-256

存入 config.versions,握手时即时生效。运维语义见 管理后台使用

文档正文以 CC BY-NC-SA 4.0 授权 · 代码部分以 GPLv3 开源 · 本项目仅作学习研究使用,与版权方无任何关联