Repository navigation
Conversation
用户指出(对的):光修入口不够,「返回键点多几次又会触发」——因为入口不止一个
(终端/Git/浏览器/设置/凭据都是 navigate 进来的,侧栏入口还是裸 navigate),
predictive back 的边缘手势还能把人带进栈根不是 chat 的怪状态。逐个修入口永远有漏网。
兜底层:MainActivity 最外层加一个 BackHandler,当
- 当前不在聊天页,且
- 没有上一层可弹(previousBackStackEntry == null,再弹就空栈)
时,返回键改为 navigate("chat") 清栈回聊天页。这样无论栈怎么被弄乱,
白屏都不可能出现;在聊天页时兜底不启用(enabled=false),返回键仍是正常的退出应用。
backQueue.size 那个写法废弃(Navigation Compose 的 backQueue 不是公开 API 语义),
改用 previousBackStackEntry == null 判「再弹就空」。
现有三层返回处理不受影响:抽屉开 → 收抽屉;大屏右栏开 → 收右栏;设置页内部层级 →
内部返回。它们 enabled 条件不满足时才轮到兜底。
用户报告:点击进入终端后,连按返回多次会触发白屏;此时右滑手势能切到设置页,
但一旦进了设置页就无法退出。
两段病因,都在窄窗(手机)导航栈上:
1. 返回栈被压穿 → 白屏。openWorkbench 窄窗分支用裸 navigate("terminal"/"git"/"browser"),
每开一次就往栈里压一层:chat → terminal → chat → terminal → …。连按返回逐层弹出,
弹空后(栈里没有任何目的地)就是白屏;此时 predictive-back 的右滑手势仍可触发
邻近的设置页,把用户带进一个"栈根不是 chat"的怪状态。
修:navigate 改为 popUpTo("chat") { saveState = true } + launchSingleTop——
无论开多少次终端,返回栈始终是 chat → terminal 一层,返回键只回聊天页。
2. 进了设置页无法退出。SettingsScreen 的 onNavigateBack(内部层级退完后的最后一跳)
原来是 popBackStack()——依赖栈里恰好有上一层;栈已被弄乱时它无事发生,留在设置页
怎么按都出不去。
修:改用 navigate("chat") { popUpTo(startDestinationRoute) { inclusive = true } } +
launchSingleTop——不依赖返回栈是否完好,总能回聊天页并把栈清干净。
大屏不受影响:openWorkbench 的 expanded 分支仍是右栏开合(不入栈)。
|
Navigate logical layers of code changes, visualize relationships, and explore their blast radius. 🧰 Additional context used📚 Code guidelines (1)No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configuration
📒 Files selected for processing (1)
Included review availability: This review used your included allowance. Your plan provides up to 2 included reviews per hour; 1 remain after this review. 📝 WalkthroughWalkthroughNavigation now returns to ChangesChat navigation
Priority: ➖ Normal Estimated code review effort: 2 (Simple) | ~10 minutes Change: Bug fix Suggested reviewers: Merge Risk: ⚪ Minimal · up to No confirmed issue currently blocks merging. Check the system Back key and gesture on a device as planned. Security Architecture ReviewSecurity architecture risk: 🔵 Low · up to The changes are confined to existing navigation paths; no new privileged destination or increased authority was identified. Saved-page restoration, process recreation, and interrupted Back behavior remain unverified. Retained concerns Security review detailsSecurity Blast Radius
Trust Boundaries and Controls
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
导航返回栈:返回键失效 / 白屏(2 条修复)
概述
这批只修导航与返回栈:2 个提交,只动
app/src/main/java/com/aicode/MainActivity.kt一个文件,+35 / -2。用户报告的现象是连在一起的:
两条提交分别对应「入口收敛」与「最外层兜底」:前者让终端 / Git 不再每开一次就压一层返回栈;
后者在最外层加一个
BackHandler,保证只要当前不在聊天页且再弹就空栈,返回键改为回聊天页而不是弹空。全部改动可从
upstream/main干净摘取(无冲突),并已通过 CI 编译验证(见文末)。修复清单
按提交顺序,每条给出:症状 → 机理 → 改法,并附原始 commit subject。
1. 最外层返回兜底:栈将空时回聊天页,而不是弹空成白屏
navigate,侧栏入口还是裸navigate),任何一个状态错乱都会让用户站在「栈根不是 chat」的位置;逐个修入口永远有漏网,predictive back 的边缘手势
还能把人直接带进设置页。
AppNavigation最外层加BackHandler(enabled = canFallbackToChat),条件为「当前路由不是
chat」+「当前目的地位于 RESUMED」+「previousBackStackEntry == null(再弹就空)」,命中时
navigate("chat") { popUpTo(startDestinationRoute) { inclusive = true }; launchSingleTop = true }。聊天页自身被显式排除,返回键仍退出应用;抽屉 / 大屏右栏 / 设置页内部层级三处既有返回处理
enabled不满足时才轮到它。顺带把「栈深度」判定从内部
backQueue改为公开语义的previousBackStackEntry == null。fix(nav): 兜底返回拦截,栈将空时回聊天页而不是弹空成白屏2. 终端 / Git 不再压穿返回栈;从预览手势进设置页后能退出
chat → terminal → chat → terminal → …),连按返回逐层弹出,弹空即白屏;此时右滑能切到设置页,而设置页的返回处理依赖「栈里恰好有上一层」,
栈已被弄乱 → 按返回无事发生,卡死在设置页。
openWorkbench窄窗分支用裸navigate(route);SettingsScreen.onNavigateBack用裸popBackStack(),两者都依赖返回栈完好。
openWorkbench改为navigate(route) { popUpTo("chat") { saveState = true }; launchSingleTop = true },无论开多少次终端栈里始终是
chat → terminal一层;设置页返回改为navigate("chat") { popUpTo(startDestinationRoute) { inclusive = true }; launchSingleTop = true },不依赖栈是否完好。大屏不受影响(
expanded分支仍是右栏开合,不入栈)。fix(nav): 终端/Git 压穿返回栈导致白屏,且从预览手势进设置页后无法退出为什么对上游有价值
风险 / 边界(请 reviewer 重点看)
BackHandler兜底改变了返回键语义——在非聊天页且返回栈只剩一层时,返回键现在是「回聊天页」而不是「退出应用」。两处保证退出路径不变:
enabled条件里显式排除currentRoute == "chat";兜底
BackHandler注册在AppNavigation前部,Compose 的返回分发是「后注册者优先」,页面级BackHandler仍优先于它。仍建议在真机上过一遍系统返回键与返回手势。
navigate("chat")而非popBackStack():会重建聊天页目的地条目。会话状态不丢,因为
AIAgentViewModel是hiltViewModel()提在NavHost之外(MainActivity.kt:358,作用域是 Activity),不随目的地条目重建;但目的地条目重建本身是行为变化,值得复核。
popUpTo("chat") { saveState = true }收敛终端 / Git 入栈:这两个页面不再多次入栈,saveState复用的是聊天页的保存状态。若后续依赖多层栈做跨页状态恢复,需要复核。previousBackStackEntry == null作为「再弹就空」的判据:这是 Navigation Compose 的公开 API 语义(替代原先依赖内部
backQueue.size的写法)。若上游将来在栈底追加别的目的地,popUpTo(startDestinationRoute)会一并清掉——
startDestinationRoute取自navController.graph,随图变化,是预期行为。本批明确不含
pr/ui-scroll-input)、单点修复如 Shizuku、终端断线横幅、附件归因(pr/single-fixes)都不在本批。
applicationId差异、CI 触发条件差异、FORK_PRIVATE清单、备份 / 记忆相关改动,一律不在本批(本批只碰
MainActivity.kt)。验证情况
upstream/main(6c615e38)。MainActivity.kt,未新增 import;改动文件的括号配平与上游版本逐一对比无失衡;改动到的符号(
BackHandler、previousBackStackEntry、navigate、launchSingleTop、saveState)在上游既有代码里已有同类用法可对照。
beta.yml手动workflow_dispatch(分支pr/nav-backstack),跑:app:assembleUniversalBeta(JDK 17 + R8 + 资源压缩)。
completed / success,attempt 1。Build universal beta APK步骤success,整个 job 08:27:21Z → 08:33:58Z(约 6 分 37 秒)。aicode-beta-3978240d5796b348a8b420a8a072ba55167acc44,25,398,423 字节(说明不止编译过了,R8 与资源处理也跑完了)。pr/nav-backstack/pr/ui-scroll-input/pr/single-fixes各一次workflow_dispatch,均success。ci.yml的单测门禁只在 push 到main/fork/release时触发,本轮只能跑 beta 编译;assembleUniversalBeta不编译app/src/test。Summary by CodeRabbit