Skip to content

fix(android): 登录轮询看门狗 + 趋势峰值 dayLabel 崩溃修复(真机 QA 发现) - #27

Closed
Qiyuanqiii wants to merge 3 commits into
mainfrom
fix/android-login-watchdog-daylabel
Closed

Qiyuanqiii wants to merge 3 commits into
mainfrom
fix/android-login-watchdog-daylabel

Conversation

@Qiyuanqiii

Copy link
Copy Markdown
Member

描述

真机 QA(#16)连续发现的第二个、第三个崩溃,同批修复:

1. 登录轮询被丢失的回调永久卡死(状态冻结在「尚未就绪」)

  • 现象:WebView 内登录成功后,localStorage 已含有效 userToken({"value":"<64字符 Bearer>"} 结构),但 App 状态永远停在「尚未就绪,等待登录完成…」;
  • 根因:evaluateJavascript 回调在页面导航期间可能被丢弃 → isChecking 永久为 true → 后续轮询全部早退;
  • 修复:checkToken / validate 引入代数(generation)+ postDelayed 看门狗(5s / 90s)——回调丢失时自动复位,下一轮询周期(1.5s)重试;过期回调按代数丢弃,避免串扰。

2. 登录成功后主页趋势图崩溃(ParseException)

java.text.ParseException: Unparseable date: "16"
    at com.deepseek.meter.app.HomeScreenKt.dayLabel(HomeScreen.kt:412)
  • 根因:dailyEntries 返回的 key 是「日」数字("16",供图表坐标标签),而峰值标签 dayLabel 用 yyyy-MM-dd 解析它(A3 遗留,首次真机有数据才暴露);
  • 修复:dayLabel 兼容日数字 key(按当月渲染「8月16日」)与完整日期 key。

附带说明

  • 登录提取逻辑本身无需改动:tokenCandidates 的 unwrap() 已正确处理新版 {"value":…} 结构(真机 + CDP 同源验证通过:unwrap 后的候选对 /users/current 返回 code 0)。

验证

  • 真机复现路径:登录后状态冻结 → 修复后 10 秒内自动完成提取并进入主页;主页渲染趋势峰值不再崩溃。

关联

变更类型

  • 🐛 Bug 修复

边界检查

  • 未引入任何第三方依赖
  • 未改动平台接口契约
  • 未夹带真实 Token 或凭据
  • 未提交构建产物
  • 未改变 Token 存储方式(保持 UserDefaults)
  • 注释与文案中文
  • 提交信息符合 Conventional Commits + 中文描述

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

一句话总结

本次 PR 修复了 Android 版登录轮询的看门狗机制(防止回调丢失导致永久卡死)以及趋势图 dayLabel 的日期解析崩溃问题,改动聚焦且方向正确。

🔴 红线 / 严重问题

未发现触碰红线的问题。

🟡 建议改进

  1. TokenLoginWebView.kt:167-172 - 看门狗超时时间硬编码

    • postDelayed({ if (gen == checkGeneration) isChecking = false }, 5000) 和 postDelayed({ if (gen == validateGeneration) isValidating = false }, 90000) 中的 5000ms 和 90000ms 是魔法数字,建议提取为具名常量(如 CHECK_TIMEOUT_MS、VALIDATE_TIMEOUT_MS),便于维护和调整。
  2. TokenLoginWebView.kt:167-172 - 看门狗回调未取消

    • postDelayed 的回调在页面销毁时不会自动取消,若 WebView 被销毁但 Timer 仍在运行,可能造成内存泄漏或空指针。建议在 onDestroy() 或 destroy() 中调用 removeCallbacks 清理。
  3. TokenLoginWebView.kt:167-172 - 看门狗与轮询周期耦合

    • 注释提到"下一个轮询周期(1.5s)会重试",但 5 秒看门狗超时意味着最多可能等待 5 秒 + 1.5 秒轮询间隔 = 6.5 秒才能重试。建议确认这个延迟是否可接受,或考虑在看门狗触发时立即触发下一次轮询。

🔵 小问题 / 风格

  1. TokenLoginWebView.kt:167-172 - 注释风格不一致

    • 新增注释使用 // 注意: 和 // 看门狗代数: 开头,与文件中其他 /// 注释风格不一致。建议统一使用 /// 或保持一致的注释风格。
  2. dayLabel 函数 - 日期解析逻辑可简化

    • dayOnly 分支中 cal.set(Calendar.DAY_OF_MONTH, dayOnly) 后直接使用 cal.get(Calendar.MONTH),但 Calendar.getInstance() 默认是当前日期,直接设置 DAY_OF_MONTH 可能产生跨月问题(如当前是 8 月 31 日,设置 DAY_OF_MONTH=16 会得到 8 月 16 日,但若当前是 9 月 1 日,设置 DAY_OF_MONTH=16 会得到 9 月 16 日)。建议明确设置 cal.set(Calendar.MONTH, ...) 或使用 LocalDate 替代。
  3. TokenLoginWebView.kt:167-172 - 字段声明顺序

    • 将状态字段移到 init 之前是正确做法,但 pollTimer 等字段与 popupCloseButton 之间缺少空行分隔,建议增加空行提高可读性。

审查结论

改动方向正确,修复了真机 QA 发现的两个实际问题(回调丢失导致轮询卡死、日期解析崩溃)。建议合并前处理以下事项:

  • 提取看门狗超时时间为具名常量
  • 补充 WebView 销毁时的回调清理逻辑
  • 确认 dayLabel 中 Calendar 跨月边界场景的处理

其余改动符合仓库规范,未触碰红线。

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

一句话总结

该 PR 修复了 Android 端两个真机 QA 发现的问题:登录轮询看门狗机制防止回调丢失导致永久卡死,以及趋势图 dayLabel 对纯数字日期 key 的解析崩溃。

🔴 红线 / 严重问题

未发现触碰红线的问题。改动仅涉及 Android 端(android/ 目录),符合移动端红线 11-13 的约束:零第三方依赖、核心逻辑在 :core 模块、未改动 Token 存储方式。

🟡 建议改进

  1. android/.../TokenLoginWebView.kt:240 — 看门狗复位逻辑存在竞态窗口
    postDelayed({ if (gen == checkGeneration) isChecking = false }, 5000) 中,如果回调在 5 秒内正常返回,isChecking 已被置为 false,但 checkGeneration 未递增。若此时恰好有新的轮询周期开始(1.5s 间隔),gen 仍等于 checkGeneration,5 秒后看门狗会错误地将 isChecking 复位为 false,而此时可能正处于新的检查中。建议在回调正常返回时也递增 checkGeneration,或在看门狗回调中增加 isChecking 状态判断。

  2. android/.../TokenLoginWebView.kt:277 — validateGeneration 看门狗 90 秒超时可能不够
    注释提到"校验链路极端情况下最长约 90 秒",但看门狗恰好设为 90 秒。如果网络极端慢或执行器队列积压,可能刚好超过 90 秒导致误复位。建议将超时设为 100-120 秒,留出安全余量。

  3. android/.../dayLabel.kt:411-416 — 纯数字 key 的月份处理逻辑
    cal.set(Calendar.DAY_OF_MONTH, dayOnly) 只设置了日,没有设置月份。Calendar.getInstance() 默认是当前月份,如果数据是历史月份(如查看上月趋势),会显示错误的月份。建议从 dailyEntries 的上下文获取正确的月份,或考虑传入月份参数。

🔵 小问题 / 风格

  1. android/.../TokenLoginWebView.kt:160-168 — 字段声明位置注释过长
    字段声明前的注释块有 4 行,解释了为什么字段要放在 init 之前。建议精简为 1-2 行,或在类头部统一说明,避免代码可读性下降。

  2. android/.../dayLabel.kt:411 — 魔法数字 "16" 示例
    注释中 "16" 的示例很好,但建议同时说明 "2026-08-16" 格式的兼容性来源(是旧版本遗留还是新格式),方便后续维护。

审查结论

改动方向正确,针对真机 QA 发现的两个问题给出了合理的修复方案:

  • 看门狗机制有效解决了回调丢失导致的轮询卡死问题,代数(generation)方案设计合理
  • dayLabel 的兼容处理避免了 ParseException 崩溃

建议合并前重点确认:

  1. 看门狗复位逻辑的竞态窗口(建议改进第 1 点)
  2. dayLabel 的月份上下文问题(建议改进第 3 点)
  3. 本地运行 cd android && ./gradlew :core:test 确认核心单测通过

整体质量良好,修复方案务实,无红线问题,可以合入。

@Qiyuanqiii

Copy link
Copy Markdown
Member Author

因 #26 合并导致分支冲突(沙箱无 git 传输通道,无法做本地 merge),已基于当前 main 重建分支并改为 #28,本 PR 关闭。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant