应用更新检测与下载实现
约 1104 字大约 4 分钟
2026-03-26
本文完整说明 SleepIn 当前“检查更新 + 弹窗展示 Release Notes + 下载 APK”的实现方案,覆盖业务目标、架构拆分、关键代码入口、状态流转和验证方法。
1. 功能目标与约束
1.1 业务目标
- 自动检测 GitHub 仓库是否发布了新版本。
- 检测到新版本后,向用户展示更新弹窗。
- 弹窗中展示发布说明(Release Notes)。
- 用户点击“下载更新”后,直接使用系统
DownloadManager下载 APK,不跳转浏览器。
1.2 技术约束
- 继续使用现有 manual DI,不引入 Hilt。
- 不影响课表主流程,更新逻辑必须可独立失败。
- 更新状态通过
DataStore持久化,确保 UI 与后台任务共享同一份状态。 - 后台任务通过
WorkManager,满足进程被杀后仍可执行的能力。
2. 架构落位
2.1 Domain 层
domain/model/AppUpdateCheckResult.ktAppReleaseInfo:标准化 release 元数据(版本、标题、notes、下载地址)。AppUpdateCheckResult:检查结果(有更新、已最新、失败、跳过)。
domain/repository/UpdateRepository.kt- 抽象更新检查接口:
checkForUpdate(currentVersionName: String)。
- 抽象更新检查接口:
domain/usecase/settings/PerformUpdateCheckUseCase.kt- 调用
UpdateRepository检查更新。 - 将结果写回
AppSettings(是否有更新、release notes、最近错误、检查时间等)。
- 调用
2.2 Data 层
data/update/GitHubUpdateRepositoryImpl.kt- 调 GitHub API:
/repos/Kurosu-Ti01/SleepIn/releases/latest。 - 解析
tag_name、name、body、assets、html_url。 - 优先选择 universal APK,否则选择第一个 APK 资产。
- 调 GitHub API:
data/update/VersionNameComparator.kt- 比较本地版本与远端版本(兼容
v1.2.3形式)。
- 比较本地版本与远端版本(兼容
data/repository/SettingsRepositoryImpl.kt- 将更新状态读写到 DataStore。
- 支持设置备份导入导出中的更新字段。
2.3 App / UI / Worker 层
update/UpdateCheckScheduler.kt- 管理周期检查与立即检查任务。
update/UpdateCheckWorker.kt- 后台执行检查更新 use case。
MainActivity.kt- 根层监听
settings。 - 满足条件时弹出“发现新版本”弹窗,展示
latestReleaseNotes。
- 根层监听
update/ApkDownloadManager.kt- 封装
DownloadManager下载入队逻辑。
- 封装
ui/screen/settings/SettingsScreen.kt- 手动“立即检查”和“下载更新”入口。
3. 关键状态模型
更新能力依赖 AppSettings 中的以下字段:
autoCheckUpdateEnabled: 是否允许后台自动检查。updateAvailable: 当前是否有可用更新。latestRemoteVersion: 远端最新版本标签。latestReleaseNotes: 远端 release notes 文本。latestApkDownloadUrl: APK 下载链接。dismissedUpdateVersion: 用户“稍后”或已确认下载的版本,用于避免重复弹窗。lastUpdateCheckError: 最近一次检查错误信息。lastUpdateCheckAtMillis: 最近一次检查时间戳。
4. 端到端数据流
4.1 自动检查链路
SleepInApplication启动后观察autoCheckUpdateEnabled。UpdateCheckScheduler.syncPeriodic(...)同步 WorkManager 周期任务。UpdateCheckWorker执行PerformUpdateCheckUseCase。- use case 将结果写入
AppSettings。 MainActivity通过observeSettingsUseCase()感知更新状态变化。
4.2 手动检查链路
- 设置页点击“立即检查”。
SettingsViewModel.checkForUpdates()以force = true调用 use case。- 检查结果更新 DataStore,页面与根层弹窗自动刷新。
4.3 弹窗与下载链路
MainActivity判断:updateAvailable == truelatestApkDownloadUrl非空latestRemoteVersion非空latestRemoteVersion != dismissedUpdateVersion
- 显示更新弹窗并渲染
latestReleaseNotes。 - 用户点击“下载更新”后:
- 调用
ApkDownloadManager.enqueueApkDownload(...)。 - 写入
dismissedUpdateVersion = latestRemoteVersion,避免重复弹窗。
- 调用
- 下载进度与完成通知由系统
DownloadManager管理。
5. GitHub 解析策略
当前解析策略如下:
- 仅使用
latest release接口。 - 如果 release 是
prerelease,默认不作为升级目标。 assets选择优先级:- 文件名包含
universal且后缀.apk - 第一个后缀
.apk的资产
- 文件名包含
body直接作为 release notes 文本显示。
6. 异常处理与降级
- 网络错误、API 错误、解析错误统一落入
AppUpdateCheckResult.Failed。 - 失败不会影响主业务功能,仅更新
lastUpdateCheckError与时间戳。 - 若未找到 APK 资产,不展示可下载更新入口(避免无效按钮)。
7. 测试与验证建议
7.1 已有单元测试
app/src/test/java/com/kurosu/sleepin/data/update/VersionNameComparatorTest.ktapp/src/test/java/com/kurosu/sleepin/data/repository/SettingsRepositoryImplTest.kt
7.2 手工验证清单
- 在可联网环境手动点击“立即检查”,确认状态刷新。
- 模拟有新版本场景,确认启动后弹窗出现且显示 release notes。
- 点击“稍后”,重进应用确认同版本不重复弹窗。
- 点击“下载更新”,确认系统下载列表中出现 APK 任务。
- 断网后检查,确认不会崩溃且会记录错误信息。
8. 后续扩展建议
- 增加“下载完成后一键安装”引导(监听下载完成广播)。
- 细化 release notes 渲染(支持 Markdown 基础样式)。
- 增加“忽略此版本”与“重新提醒”的显式设置项。
- 为 GitHub API 增加更细粒度重试与限流提示。
更新日志
2026/5/18 12:59
查看所有更新日志
2a526-docs(readme): update screenshot layout于
