bbc47dbd314fb96ab599fbbd0ed868813c4d1986
S4 的病根:每个访问过的会话各留一个常驻 VM(都订阅那条共享连接 ⇒ 每次重连各自发一轮 after 补拉)、各留一份消息列表(含内联 base64 图片),而删会话也不回收。此前**没有按会话 交出一格的手段**:所有 VM 塞在 Activity 级那一个 ViewModelStore 里,而它只有整体 clear(), W48 只能做到「让 key 换一代、旧实例继续留着」—— 旧实例照样活着、照样收帧。 这次把「哪几格留着」这件事单独拎出来: - 新增 `core/session/ChatSessionSlots.kt`(进程级,挂在 AppContainer 上,与 GoneSessions 同一层):一格一份 ViewModelStore。`ChatScreen` 改成 `viewModel(viewModelStoreOwner = <槽位>, key = chatViewModelKey(...))`,所以「交出去一格」 就是那一份 store 自己的 `clear()` ⇒ 那一格的 `onCleared()` + `viewModelScope` 取消 (订阅、补拉、在飞的请求一起停)。共享连接的生死一个字节都没碰:主人仍是 SessionsViewModel,`ChatViewModel.onCleared` 照旧**不** close(现在它会真的被调到, 所以这句注释也一并写成事实)。 - 新增判据 `core/session/chatSessionEvictions`(**纯函数**,VM 与测试共用): 「最近」的口径 = **最后一次成为当前会话的时刻**(由 ChatScreen 在组合到某个会话时写入); 候选要同时满足「不是主会话 ∧ 不是屏幕上那一格 ∧ 没有没交代完的事」,按最旧的先走, 保留最近 `keep` = 3 格(主人口径,含被豁免的那些占的名额——不然「最多 3 格」会变成 3 + 豁免数)。两条豁免名额之外的豁免各自都有硬理由:屏幕那一格死了会「不再更新、连 发送都静默不发」,还有没交代完的事那一格(草稿 / 排队等连接的发送)死了内容**回不来**。 - 删会话时那一格当场交出去(`SessionsViewModel.deleteSession`,与 W51 的 `goneSessions.mark` 相邻:一个是「别发注定 404 的补拉」,一个是「这一格本来就不该留」); 登出 / 换账号时 `clear()` 一格不留(与快照 / 已读游标 / GoneSessions 同档纪律)。 - 「还有没交代完的事」收的是**现读的探针**而不是打开那一刻的快照:回收发生在别的会话被 打开的那一刻,而那正是用户可能刚在前一格打了草稿、还没切走的时候。 刻意不动的红线:W48 的 `chatViewModelKey` 拼法一个字没改(新槽位的 key 里同样带账号指纹, 两道闸门各管一段);W49 的节流判据、W50 的 ProactiveIngest 判据、W51 的 reconnectPull 判据都没碰,也没有把「连接恢复」做成整表重拉。 测试:新增 `ChatSessionEvictionTest`(14 例,穷举判据的每一条边界:N=3、最旧先走、主会话 / 屏幕上那一格 / 有草稿的豁免、豁免占名额、并列退回账本顺序、结论是带账号指纹的槽位 key) 与 `ChatSessionSlotsTest`(9 例,钉住「没被收的一格还是原来那一格(切回来状态还在)」与 「被收的一格下次拿到的是新的一格」、空槽位会被下一次回收扫掉、探针现读的效果)。 同批把已经说错的注释改成事实:GoneSessions / AppContainer.chatSocket / ChatHomeScreen / ChatScreen 里那几处「VM 活在 Activity 级 ViewModelStore 里、切走 / 删会话都不回收」。
Releases
5
Alpha_v0.1.0_4
Latest