HarmonyOS 7 端 A2A 页面退出后还收到回调:监听作用域、取消令牌和迟到消息怎么收口

用户在智能搜索页发起一次端 A2A 请求,马上返回上一页。几秒后结果回调仍然到达,旧页面对象继续写状态,轻则弹出一个不该出现的提示,重则把新页面的数据覆盖掉。问题不在“回调慢”,而在请求生命周期和页面生命周期被当成了同一件事。本文给出一套可以复现、可以断言、也方便复用的收口方式。

端 A2A 页面生命周期与回调去向

验证范围:官方资料用于确认能力方向和版本边界;文中的 TypeScript 代码验证应用侧状态模型。当前电脑安装的是 API 24 SDK,且没有连接 HarmonyOS 7 真机,因此本文不把本机断言写成 API 26 编译或真机实测。接入时仍要以目标版本文档、真实设备日志和平台返回值为准。

先把故障稳定复现出来

让 Agent 响应固定延迟 3 秒;页面进入时注册监听,1 秒后返回;随后再次进入同一页面并发起新请求。如果旧回调没有携带会话身份,或者页面退出只移除了 UI 监听却没有关闭业务会话,旧结果就可能落进新页面。日志至少要同时打印 sessionId、requestId、页面实例号和收到回调的时间。

案例一:只读建议可以忽略迟到结果

菜谱建议、知识问答这类只读结果没有外部副作用。页面退出后把会话标记为 detached,迟到消息只进入诊断记录,不再更新 UI。用户重新进入时创建新 session,而不是复用上一次页面实例。

案例二:已经触发设备动作不能只丢消息

如果 Agent 已经发起下载、播放或跨设备控制,页面退出不代表动作停止。此时取消令牌需要得到业务层确认;取消失败时仍要保存终态,供下一次进入页面恢复,而不是简单丢弃回调。

根因不只是忘了取消监听

监听器、请求和页面有三条生命周期。只在 aboutToDisappear 里删监听,解决不了已经排队的消息;只设置一个 isPageAlive 布尔值,又无法区分旧页面和新页面。更稳的做法是让每次页面会话拥有独立 token,并让业务层保存终态。

type SessionState = 'active' | 'detached' | 'closed';
class SessionGate {
  private state: SessionState = 'active';
  constructor(private readonly token: string) {}
  detach() { this.state = 'detached'; }
  close() { this.state = 'closed'; }
  accept(token: string, terminal: boolean): 'ui' | 'store' | 'ignore' {
    if (token !== this.token || this.state === 'closed') return 'ignore';
    if (this.state === 'detached') return terminal ? 'store' : 'ignore';
    return terminal ? 'store' : 'ui';
  }
}
const gate = new SessionGate('page-2');
gate.detach();
if (gate.accept('page-1', true) !== 'ignore') throw new Error('旧会话越界');
if (gate.accept('page-2', true) !== 'store') throw new Error('终态没有保存');

这段代码只做一件事:把旧会话、离屏会话和已关闭会话分开。它不代替平台 API,却能把最容易写错的状态判断从页面代码里抽出来,先在本机用确定输入验证,再放进设备联调。

三种处理法放在一起比较

方案适合什么情况主要代价
页面布尔值原型验证页面重建时容易串实例
取消令牌请求可取消且回调可识别远端不一定承诺立即停止
会话控制器有恢复、重连和跨页面需求需要维护状态账本

我会把取消令牌和会话控制器一起用:取消令牌表达“不要继续做”,会话控制器表达“即便结果回来了也应该放到哪里”。两者解决的不是同一个问题。

把页面细节收进会话控制器

可以封装 AgentSessionController,对外只暴露 start/detach/cancel/close/onMessage。页面层不直接保存监听句柄,也不判断业务终态。下载任务、跨设备命令和长耗时推理都能复用这套边界。

上真机前逐项验收

  • 页面实例、sessionId、requestId 能在日志中关联。
  • 退出页面后进度消息不会再改 UI。
  • 有副作用的终态不会因为离屏而丢失。
  • 重新进入页面不会收到上一实例的导航事件。
  • 取消无确认时不会假装任务已经停止。

以后遇到“页面退出了回调还来”的问题,不要只补一个判空。先画清三条生命周期,再决定消息是更新 UI、保存终态还是忽略。

官方资料

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐