新增 docs/android/ 目录: - README.md 总入口(快速上手 + 决策摘要 + 数据流) - 01-architecture.md 模块划分 + 数据流 + 选型理由 - 02-api-contract.md 每个接口的请求/响应 + DTO 字段映射 - 03-build-run.md Gradle/SDK/网络安全白名单/真机调试 - 04-milestones.md 7 天里程碑 + DoD + E2E 测试场景 新增 assets/: - logo/: 主图标 master + adaptive icon + 5 DPI launcher (方/圆) - splash/: 启动屏 logo + 完整背景预览 + 5 DPI 资源 - android_resources/: 集成所需的 XML(adaptive icon/主题/颜色/字符串/drawable/layout) - INTEGRATION.md 集成指南 - logo.svg + _make_logo.py 设计源 设计风格:参考用户提供的木质方块字母积木图,米色木纹底 + 深棕色字母 D,代表 'Diary',温暖私人日记感。 服务器体检:所有容器/API/DB/翻译主链路正常,TMT 本月已用 0.37%。 MaaS 备用通道上次已验证可用。
10 KiB
10 KiB
04 · 7 天里程碑
目标:第 7 天能装一个 APK 到真机,跑通 登录 → 列表 → 详情 → 收藏 → 离线缓存 完整链路。
工作量估计基于:全栈老哥(你已经会 Kotlin/Compose 基础),按一天 3-4 小时有效开发时间算。
每个里程碑有明确 DoD(Definition of Done)。不达 DoD 不算完成。
全局 DoD(贯穿 7 天)
./gradlew assembleDebug每次都 BUILD SUCCESSFUL- 真机/模拟器能装能跑,不闪退
git commit节奏:每个里程碑一个 commit,信息写清楚- logcat 无 ERROR 级别的 crash
- 没有 hardcoded 颜色 / 字体大小(全部走
MaterialTheme)
Day 1 — 工程骨架 + 鉴权三件套
目标:能登录、能持久化 token、401 自动 refresh、空 UI 能跳转。
任务清单
| # | 任务 | 文件 | 估时 |
|---|---|---|---|
| 1 | 新建 Android Studio 工程,改 libs.versions.toml + app/build.gradle.kts |
见 03 | 30min |
| 2 | 配 network_security_config.xml + 4 个 res 文件 |
见 03 §4 | 15min |
| 3 | DiaryNewsApp.kt(@HiltAndroidApp) |
app/ |
5min |
| 4 | MainActivity.kt(@AndroidEntryPoint,装 AppNav) |
app/ |
15min |
| 5 | DTO 文件 12 个 | data/api/dto/ |
1h |
| 6 | ApiService.kt |
data/api/ |
30min |
| 7 | TokenStore.kt(EncryptedSharedPreferences) |
data/auth/ |
30min |
| 8 | AuthInterceptor.kt + TokenAuthenticator.kt |
data/api/ |
1h |
| 9 | NetworkModule.kt(Hilt) |
di/ |
30min |
| 10 | LoginScreen.kt + LoginViewModel.kt |
ui/login/ |
1h |
| 11 | AppNav.kt 骨架(Login ↔ Feed) |
ui/nav/ |
15min |
| 12 | Theme.kt + Color.kt + Type.kt(复用 web 的蓝 #2080f0) |
ui/theme/ |
30min |
Day 1 DoD
./gradlew assembleDebugBUILD SUCCESSFUL- 装到真机,启动能看到登录页
- 输入
owner / test1234,点登录 → 跳到一个空白 Feed 页 - 杀掉 app 再开 → 直接进 Feed 页(token 持久化生效)
- 等 60min 后,任意接口请求 → 自动 refresh → 用户无感
- logcat 里能看到
OkHttp打印的请求日志(debug build) - 进
设置 → 应用 → Diary News → 存储,看不到明文 token(只在加密 SP 里)
验证方法
# Day 1 验证清单(贴在 issue / PR 描述里):
- [x] 登录成功,TokenStore 保存到 EncryptedSP
- [x] 重启 app 自动进 Feed(读 SP 成功)
- [x] 改密码 → 旧 token 在 server 失效 → app 收到 401 → 跳登录
- [x] 等 access 过期(60min)→ 任意请求 → 自动 refresh → 200
Day 2 — Feed 列表(分页 + 卡片)
目标:能滚能动,看到真实的新闻列表,卡片视觉对齐 web。
任务清单
| # | 任务 | 文件 | 估时 |
|---|---|---|---|
| 1 | ArticlePagingSource.kt |
ui/feed/ |
30min |
| 2 | FeedViewModel.kt(Paging 流) |
ui/feed/ |
30min |
| 3 | ArticleCard.kt(对齐 web 视觉) |
ui/common/ |
1h |
| 4 | FeedScreen.kt(LazyColumn + 加载状态) |
ui/feed/ |
1h |
| 5 | 源筛选 + 关键词搜索(下拉 + 输入) | ui/feed/ |
1h |
| 6 | 空状态 / 错误状态 / 加载状态组件 | ui/common/ |
30min |
Day 2 DoD
- 列表能加载第一页(50 条)
- 滑到底自动加载下一页(Paging 3 自动)
- 卡片展示:源 / 语言 / 分类 tag / 时间 / 原标题(灰)/ 中标题 / 插图 / 译文正文摘要 / 评论钩子
- 源筛选:选一个源,列表只剩该源
- 关键词搜索:输入 "AI" → 列表过滤
- 无网络时显示空状态 + 重试按钮
- 滚得快时图片不卡(Coil 默认就 OK)
视觉对齐清单
对照 web frontend/src/views/Feed.vue:
- 卡片圆角、间距、字号 1:1
- 中文标题字号 18sp,原标题 13sp 灰
- 评论钩子块背景
#f6f8ff,左边框#2080f03dp
Day 3 — 详情页(三段 Tab)
目标:点卡片进详情,看到完整译文 + 评论 + 原文。
任务清单
| # | 任务 | 文件 | 估时 |
|---|---|---|---|
| 1 | ArticleDetailDto → domain.ArticleDetail mapper |
data/ |
30min |
| 2 | ArticleViewModel.kt(StateFlow<UiState>) |
ui/article/ |
30min |
| 3 | ArticleScreen.kt 骨架 + TabRow |
ui/article/ |
30min |
| 4 | ArticleTabs.kt:CommentaryTab / TranslationTab / OriginalTab |
ui/article/ |
1h |
| 5 | TranslationTab:WebView 渲染 body_zh_formatted(无 JS) |
ui/article/ |
1h |
| 6 | OriginalTab:WebView 渲染 body_html(无 JS) |
ui/article/ |
15min |
| 7 | 详情页顶部卡片(标题 / 分类 / 时间 / 插图) | ui/article/ |
30min |
| 8 | 收藏按钮 ☆(Day 4 接 API,Day 3 先显示静态) | ui/article/ |
15min |
Day 3 DoD
- 点列表卡片 → 进详情页
- 顶部展示:原标题(灰)/ 中标题(主)/ 插图 / 分类 tag / 发布时间 / 作者
- 三个 Tab 可切换:评论 / 译文 / 原文
- 译文 Tab 用
body_zh_formatted,如果没有就 fallback 到body_zh_text - WebView 不开 JS(验证方式:
runJavaScript调用失败) - 详情页旋转屏幕 / 切换深色模式不崩
Day 4 — 收藏(乐观更新 + Room 缓存)
目标:能收藏 / 取消,UI 秒响应,失败可回滚。
任务清单
| # | 任务 | 文件 | 估时 |
|---|---|---|---|
| 1 | Room:AppDatabase.kt + BookmarkDao.kt + BookmarkCacheEntity |
data/db/ |
1h |
| 2 | BookmarkRepository.kt(乐观更新) |
data/repository/ |
1h |
| 3 | DatabaseModule.kt(Hilt) |
di/ |
15min |
| 4 | 详情页收藏按钮接 ArticleViewModel.toggleBookmark() |
ui/article/ |
30min |
| 5 | BookmarksScreen.kt + BookmarksViewModel.kt |
ui/bookmarks/ |
1h |
| 6 | 离线显示:无网络时用 Room 缓存渲染 | ui/bookmarks/ |
30min |
Day 4 DoD
- 详情页点 ☆ → 立刻变实心(乐观更新)
- 后台调
POST /bookmarks,失败时 → 回滚 ☆ + Snackbar "收藏失败,已撤销" - 收藏列表页能看到所有收藏
- 离线时收藏列表仍可看(读 Room 缓存)
- 滑动列表时无卡顿(用
LazyColumn+key)
Day 5 — 源 + 订阅
目标:能看所有源、订阅 / 取消订阅。
任务清单
| # | 任务 | 文件 | 估时 |
|---|---|---|---|
| 1 | SourceRepository.kt |
data/repository/ |
30min |
| 2 | SourcesScreen.kt + SourcesViewModel.kt |
ui/sources/ |
1h |
| 3 | 订阅按钮 + POST/DELETE /subscriptions |
ui/sources/ |
1h |
| 4 | 底部导航栏:Feed / Sources / Bookmarks | ui/nav/ |
1h |
Day 5 DoD
- 底部三 Tab:Feed / Sources / Bookmarks
- Sources 页能看到所有 enabled 源,带订阅状态
- 点订阅按钮 → 立刻变化 + 后台请求
- 失败 Snackbar 提示 + 回滚
- Feed 页源筛选多选能跨 Tab 状态保留(navigation 状态管理)
Day 6 — 主题 + 通用打磨
目标:视觉统一、空状态友好、加载体验好。
任务清单
| # | 任务 | 文件 | 估时 |
|---|---|---|---|
| 1 | Material3 主题:浅色 + 深色(Dynamic Color,Android 12+) | ui/theme/ |
1h |
| 2 | 自定义启动屏 + 应用图标 | res/ |
30min |
| 3 | 通用加载 / 空 / 错误组件统一风格 | ui/common/ |
30min |
| 4 | 下拉刷新(SwipeRefreshLayout / PullToRefreshContainer) |
ui/feed/ |
30min |
| 5 | 网络状态监听(ConnectivityManager) | data/ |
30min |
| 6 | 离线条 / Snackbar 提示 | ui/common/ |
30min |
| 7 | 长按卡片 → 分享 / 复制 URL 菜单 | ui/common/ |
30min |
Day 6 DoD
- 切深色模式 → 全 app 颜色自动切(阅读体验好)
- 启动屏不闪白(用 SplashScreen API)
- 列表支持下拉刷新
- 飞行模式下进入 app → 各页有合理提示
- 长按文章卡片弹出菜单:复制链接 / 在浏览器打开 / 分享
Day 7 — 收尾 + 真机端到端测试 + APK 打包
目标:出第一个 release-ready APK,完整跑一遍所有功能。
任务清单
| # | 任务 | 文件 | 估时 |
|---|---|---|---|
| 1 | README.md(装 app 说明 + 调试指南) |
根目录 | 30min |
| 2 | ProGuard / R8 配置 + release build 验证 | proguard-rules.pro |
1h |
| 3 | 真机端到端测试(用 e2e-checklist.md 列表) | — | 2h |
| 4 | crash 报告接入(可选:Sentry / Firebase Crashlytics) | — | 1h |
| 5 | 修最后发现的 bug | — | 1h |
Day 7 DoD
./gradlew assembleRelease出app-release-unsigned.apk- debug APK 跑完下面所有场景,全过
E2E 测试场景清单
- 第一次启动 → 登录页
- 登录成功 → 进 Feed
- 列表能加载,滑到底加载更多
- 源筛选 / 关键词搜索生效
- 点卡片进详情,三段 Tab 都能切
- 收藏 / 取消收藏 UI 秒响应,后台请求成功
- 收藏页能看到刚收藏的
- 杀进程,重启,直接进 Feed(token 持久化)
- 飞行模式启动,各页空状态 + 离线缓存(收藏)仍可用
- access 过期后任意请求,自动 refresh,UI 无感
- 改密码,旧 token → app 跳登录
风险 & 应对
| 风险 | 概率 | 影响 | 应对 |
|---|---|---|---|
| Compose / Kotlin 版本不兼容,build 失败 | 中 | 高 | 严格用本文档指定版本;失败先查 Compose-Kotlin Compatibility Map |
| 后端 schema 改了,app 跑不起来 | 中 | 中 | 启动时 catch SerializationException,跳错误页 + 重启按钮 |
| 真机 7.0 / 7.1 系统太老,某些 API 没有 | 低 | 中 | 用 AndroidX 兼容层;遇到问题查 API 24 compat matrix |
| 网络安全白名单加错,全 app 报错 | 低 | 高 | 第 1 次 build 一定先跑清单 Day 1 DoD |
| 11 章(ProGuard)没做就 release,接口全找不到 | 中 | 高 | Day 7 必须先跑 ./gradlew assembleRelease 再装机 |
| EncryptedSharedPreferences 在某些定制 ROM 上崩 | 极低 | 中 | catch GeneralSecurityException,fallback 引导用户重新登录 |
后续迭代方向(7 天后看)
| 优先级 | 功能 | 估时 |
|---|---|---|
| P1 | FCM 推送(新文章到时通知) | 2-3 天 |
| P1 | 阅读历史(本地) | 半天 |
| P2 | 平板 adaptive(WindowSizeClass + 双栏) | 1-2 天 |
| P2 | 离线下载包(整周报导出) | 2-3 天 |
| P3 | Wear OS 端 | 1 周 |
| P3 | 主屏 widget | 2-3 天 |
| P3 | 多账号切换 | 1 天 |
任务量快速核算
| Day | 估时 | 主要内容 |
|---|---|---|
| 1 | 7h | 工程 + 鉴权 |
| 2 | 4.5h | Feed + 分页 + 卡片 |
| 3 | 4.5h | 详情 + WebView |
| 4 | 4h | 收藏 + Room |
| 5 | 3.5h | 源 + 订阅 + 底导 |
| 6 | 4h | 主题 + 通用 |
| 7 | 5.5h | 测试 + APK |
| 总计 | ~33h | 7 个有效工作日 |