把 APK 放到网站并提供一个下载按钮,看起来已经完成发布。真正困难的部分会在下一次更新时出现:新包能否覆盖旧包、用户如何确认文件没有损坏、签名密钥是否还能找到,以及新版 Android 的开发者验证是否已经准备好。

Debug APK 不是长期发布包

assembleDebug 生成的 APK 会使用 Android SDK 自动创建的 debug 证书,并允许调试。它适合开发机、模拟器和临时测试,不适合作为长期公开下载。debug keystore 本来就是不安全的开发凭据,应用商店也不会把 debug 签名当作正式发布身份。

公开版本应使用 release variant,并由自己的长期密钥签名:

./gradlew clean assembleRelease

更新身份由三个值共同决定

一个已安装应用能否被新 APK 原地更新,至少取决于:

  1. applicationId 与已安装应用一致;
  2. 新 APK 的签名证书与既有版本匹配,或符合已建立的密钥轮换关系;
  3. versionCode 高于设备上的版本。

versionName 是展示给用户看的字符串,versionCode 才是 Android 比较新旧的正整数。把两者都写进发布记录,但不要用文件名或 versionName 代替系统版本判断:

android {
    defaultConfig {
        applicationId = "com.example.puzzle"
        versionCode = 12
        versionName = "1.3.0"
    }
}

更换签名证书而没有正确的轮换方案,通常会让系统把新包视为不能覆盖旧应用的另一个发布身份。用户只能卸载旧版再安装,现有本地数据也可能随之丢失。因此,签名密钥的备份策略与源代码备份同样重要。

密钥不能进入仓库

release keystore、别名和密码不应提交到 Git。可以让 CI 从受保护的 secret 注入临时文件和环境变量,构建结束后清理工作目录。密钥本体至少保留一份离线加密备份,并记录恢复步骤,而不是只依赖某台开发电脑。

发布前验证 APK,而不是相信文件名

Android SDK Build Tools 中的 apksigner 可以检查签名是否适用于 APK 支持的 Android 版本,并输出证书信息:

apksigner verify \
  --verbose \
  --print-certs \
  app-release.apk

CI 应保存预期的 SHA-256 证书指纹,并在每次构建后比对。这样能及时发现构建机使用了错误 keystore。注意签名后再修改 APK 会破坏签名;如果流程单独运行 zipalign,必须先对齐再签名。

还应读取实际制品的包名、versionCode、versionName、最低 SDK 和是否可调试,而不是只检查 Gradle 文件。构建配置、flavor 或缓存都可能让最终 APK 与预期不同。

SHA-256 校验的是文件,不是开发者身份

为下载文件发布 SHA-256 很有价值:用户或自动化可以确认下载没有截断,也能判断服务器上的 APK 是否与本次发布制品完全相同。

shasum -a 256 app-release.apk

但如果攻击者能同时替换 APK 和网页上的哈希值,校验仍会“通过”。哈希解决完整性核对,APK 签名负责更新身份,HTTPS 保护传输;三者不能互相代替。更完整的发布记录应包含文件大小、SHA-256、签名证书 SHA-256 指纹、版本号和发布日期。

下载 URL 需要兼顾稳定与可追溯

直接覆盖 app.apk 方便,却让缓存、故障回滚和问题定位变得困难。更稳妥的方式是保存不可变的版本化文件,例如:

/downloads/puzzle-1.3.0-12.apk

页面上的“最新版本”按钮可以更新到这个文件,旧 URL 则保留一段时间。服务器应返回正确的内容长度和 APK MIME 类型 application/vnd.android.package-archive,避免 HTML 错误页以 200 OK 冒充 APK。部署后应从公网检查状态码、大小和 SHA-256。

发布页面也属于产品

用户从网站安装应用时,看不到商店自动提供的信息。下载页应该清楚显示应用名称、平台要求、版本、发布日期、更新说明、文件大小和校验值,并提供技术支持与隐私政策。安装未知来源应用所需的系统提示也应如实说明,不要诱导用户关闭设备安全功能。

同时测试全新安装和从当前公开版本升级。升级测试要使用真实旧版 APK 并保留数据,才能发现签名、版本号或数据库迁移问题。

2026 年还要准备开发者验证

截至 2026 年 9 月 18 日,Android 开发者验证已开放。9 月 30 日起,首阶段会在巴西、印度尼西亚、新加坡和泰国的认证设备上,针对参与的应用商店实施;官方计划从 2027 年开始扩展到全球认证设备。

官方 FAQ 说明,2026 年 9 月的首阶段尚不直接覆盖网站侧载,但建议在全球推广前完成验证。只在 Google Play 之外分发的开发者使用 Android Developer Console;同时使用 Play 的开发者可在 Play Console 管理相关包名。注册包名可能需要用既有私钥证明所有权,这再次说明签名密钥不能遗失。

把发布做成可重复流水线

一次可靠发布可以固定为以下顺序:

  1. 更新并检查 versionCode 与 versionName;
  2. 从干净提交构建 release APK;
  3. 使用受保护的长期密钥签名;
  4. 用 apksigner verify --print-certs 验证签名和证书;
  5. 检查包名、版本、SDK、权限和 debuggable 标志;
  6. 运行自动测试,并在真实设备验证全新安装与升级;
  7. 生成 SHA-256、文件大小和发布记录;
  8. 上传版本化 APK,更新下载页面;
  9. 从公网重新下载并再次比对哈希;
  10. 保留上一个可用版本和明确的回滚步骤。

自托管 APK 的自由来自对整条交付链的控制,而不是少做几个步骤。只要签名身份、版本单调性、制品完整性和网页信息都进入自动检查,网站下载就能从一次性的文件分享,变成可以持续升级和审计的正式发布渠道。