HarmonyOS API 26 Navigation 异步入栈:pushDestination、结果回传与重复跳转治理

异步入栈不是把 pushPath 改成 await。真正需要治理的是目标能否构建、重复点击是否合并、LaunchMode 如何选择实例,以及页面返回结果由谁消费。

Navigation 异步入栈封面

一、pushDestination解决什么问题

官方 Navigation 参考提供 pushDestination(info, animated?) 与接收 NavigationOptions 的重载,返回 Promise<void>。与只表达入栈意图的同步调用相比,调用方可以接收参数错误、内部错误、Builder 未注册或目标 NavDestination 不存在等结果。

Promise fulfilled 说明这次接口调用完成,不代表目标页业务数据已经加载,也不代表用户最终完成了页面任务。

二、官方约束先落到清单

该接口仅用于 Stage 模型。官方还提醒不建议在 aboutToAppear 中执行栈操作,因为页面尚未构建完成,可能出现白屏或跳转失败。具体设备起始版本、错误对象和重载形式应以目标 API 26 SDK 类型声明为准。

await pageStack.pushDestination({
  name: 'OrderDetail',
  param: { orderId: 'masked-id' }
})

本文示例没有经过 HarmonyOS 编译。

三、入栈成功要分四个检查点

按钮被点击、请求通过应用预检、Promise 成功、目标页内容可用是四个不同节点。UI loading 可以覆盖前两段,导航监控覆盖 Promise,目标页再管理自己的 loading、empty、error、content。任何一层失败都不能篡改另一层的真实状态。

四、建立最小入栈命令

页面不应直接把任意对象交给路由。命令包含目标名称、最小参数、请求键、实例策略和来源枚举,由协调器统一校验。

interface PushCommand {
  name: string
  param: Record<string, string | number | boolean>
  requestKey: string
  source: 'tap' | 'deeplink' | 'recovery'
  forceNewInstance: boolean
}

请求键用于应用侧合并,不是系统 NavPathInfo 字段。

五、目标名称必须进入白名单

动态字符串、服务端返回值和深链路径不能直接成为页面名。先将外部意图映射到本地注册表,再生成目标名称和安全参数。未注册目标应在调用前拒绝,并保留当前页面。

六、参数只携带稳定身份

完整用户对象、订单详情或长列表容易过期,也不利于恢复和脱敏。路由参数优先携带业务 ID、模式版本与只读开关,目标页通过 Repository 获取最新数据。跨账号时还要验证会话代次,避免相同 ID 复用旧缓存。

七、重复点击首先合并任务

同一按钮快速点击两次,如果创建两个 push Promise,可能出现两个相同页面。协调器按 requestKey 保存进行中任务;相同请求共享结果,不同请求根据产品规则排队或拒绝。

const existing = pending.get(command.requestKey)
if (existing) return existing
const task = executePush(command).finally(() => pending.delete(command.requestKey))
pending.set(command.requestKey, task)
return task

完成后必须清理,失败也不能留下永久锁。

八、同名栈顶通常应拒绝

如果当前栈顶已经是同一业务详情,再 push 一次通常只会制造重复返回层级。应用可比较页面名和稳定业务键,选择刷新参数或忽略。仅比较名称会误伤同名不同实体,因此判定必须符合产品身份模型。

九、NEW_INSTANCE用于真正的新会话

官方 LaunchMode 包含 NEW_INSTANCE,可在需要时强制创建同名新实例。适合独立编辑会话或必须并存的文档窗口,不适合用来绕过重复点击。官方说明其新页面默认执行 push 动效,真实体验仍需设备验证。

十、单例模式需要新参数处理

MOVE_TO_TOP_SINGLETON 和 POP_TO_SINGLETON 可能复用已有目标。复用后页面必须响应新参数并重新核对数据身份。不能假设构造逻辑会再次运行,也不能让旧账号内容因实例复用继续可见。

异步入栈治理流程

十一、Promise错误要分类

401 属于参数或调用契约问题,不应重试;100005 指向 Builder 注册;100006 指向目标缺失;100001 内部错误最多在安全条件下有限重试。用户提示保持简洁,诊断日志记录脱敏页面别名和阶段。

十二、失败默认保留来源页

入栈失败时用户仍应停留在发起操作的页面。解除 loading、展示可操作提示并保留返回路径。不要在 catch 中清栈,也不要为了“恢复”而跳到另一个未经验证的页面。

十三、超时不等于取消

交互层可以在等待过久时结束 loading,但 Promise 仍可能迟到。使用 owner generation 判断迟到结果是否还能修改当前 UI;不得在超时瞬间无条件再 push 一次,否则两个结果会竞争。

const owner = currentOwner
await pushTask
if (owner !== currentOwner) return

十四、返回结果与入栈结果不同

push Promise 反映“进入目标页”的结果;onPop 回调或 PopInfo 反映“目标页退出并交付结果”。二者生命周期不同。调用方不能 await pushDestination 后就读取用户选择结果。

十五、onPop只传必要结果

返回值可以包含是否确认、稳定 ID 或变更版本,不应携带完整敏感对象。来源页收到结果后重新从数据层读取,并验证账号代次和页面 owner 是否仍有效。

interface SelectionResult { confirmed: boolean; itemId?: string; revision?: number }

十六、无结果返回也要定义

用户系统返回、手势退出、目标页异常关闭或被批量清栈时,来源页可能收不到业务结果。调用方必须把“无结果”视为取消或未知,而不是默认成功。需要强一致性的业务应向服务端查询最终状态。

十七、回调不能持有失效组件

来源页面离场后,onPop 可能晚到。回调先检查 owner token,再决定是否更新局部状态;需要跨页面保存的结果交给 Service 或 Repository,不能依赖已经销毁的组件实例。

十八、业务提交与入栈分离

“保存并查看详情”通常先提交业务,再入栈详情。提交要有独立幂等键。业务成功但入栈失败时展示“查看已保存内容”的重试入口,不能再次提交;业务失败则不得进入伪成功页面。

十九、深链入栈先准备基础容器

冷启动深链要等 Navigation 与路由表就绪,完成域名、路径和参数白名单后再调用。无效深链回到安全主页,不能让用户停在空容器。外部参数不得原样进入日志。

二十、恢复不重放旧Promise

进程恢复时,旧异步任务不复存在。只恢复允许的页面身份和业务状态;若业务结果未知,进入确认页查询,而不是重放提交或重复 push。恢复栈也要经过页面白名单和参数版本迁移。

二十一、父子NavPathStack要隔离

平板分栏可能由父栈管理工作区、子栈管理详情。PushCoordinator 只能操作它拥有的栈。跨容器意图由上层编排器转译,禁止子组件持有全局栈任意入栈。

异步入栈分层结构

二十二、无障碍焦点是验收项

目标页出现后,读屏焦点应落到标题或能说明当前状态的节点,而不是留在来源页按钮。键盘 Tab 顺序、返回后的焦点恢复、错误态重试按钮都需要真实 ArkUI 验证。动画关闭不代表焦点自动正确。

二十三、监控按阶段拆分

建议记录预检拒绝、请求合并、平台调用、Promise 结果、目标首帧、数据可用和 onPop 结果七个阶段。指标包括重复阻断率、各错误码数量、迟到结果数和无结果返回率。日志只留脱敏页面别名、请求键散列与版本。

二十四、纯策略模型的范围

配套 PushDestinationPolicy.ts 将空名称、未注册、同请求合并、其他请求阻断、同名栈顶和强制新实例转换为确定决策。32项主机测试覆盖优先级和边界输入。

它没有调用 NavPathStack,不证明 Promise 错误、LaunchMode、onPop 或生命周期在系统中的实际行为。

二十五、真实工程测试矩阵

  • 合法页面成功入栈;
  • 401、100001、100005、100006 错误处理;
  • 连点合并与不同目标竞争;
  • 同名同实体、同名不同实体;
  • 四种 LaunchMode;
  • onPop 有结果、无结果和迟到结果;
  • 业务成功但导航失败;
  • 冷启动深链和恢复;
  • 父子栈隔离;
  • 横竖屏、分栏、深浅色与无障碍焦点。

二十六、常见问题排查

现象常见原因治理方式
连点出现两层详情未合并 requestKey复用进行中任务
await 后取不到选择结果混淆 push 与 pop在 onPop 消费结果
单例页显示旧内容未刷新新参数按业务身份重载
失败后空白catch 中额外清栈保留来源页
返回更新已销毁页面回调缺 owner 校验使用 generation 隔离

二十七、列表到详情要保留选择快照

来源页发起入栈时,列表可能因为刷新、分页合并或筛选变化而重新排序。返回详情后如果只依赖旧索引恢复选中项,焦点会落到错误记录。协调器应保存稳定业务 ID、筛选版本和必要的滚动锚点;收到 onPop 结果后先确认记录仍存在,再恢复选择和焦点。索引只能作为临时显示信息,不能成为跨页面身份。

如果详情修改导致该记录不再满足当前筛选,来源页应明确刷新并把焦点移动到可解释的位置,例如列表标题或下一条有效记录,而不是强行滚动到已经消失的索引。此行为属于应用交互策略,需要在手机、平板和键盘环境分别验证。

二十八、动态模块页面要区分下载与入栈

当目标页面来自动态模块或按需资源时,模块准备成功和 pushDestination 成功是两个步骤。模块未就绪时不应反复调用路由接口;先由模块服务完成检查与加载,再确认 Builder 注册,最后提交入栈。下载失败保留来源页,并提供明确重试入口。

模块加载完成后也要重新校验 owner generation,因为用户可能已经离开来源页。若任务迟到,只缓存模块可用状态,不自动把页面推到当前用户面前。监控应分别记录模块耗时、路由 Promise 耗时和目标页首帧。

二十九、返回结果消费要具备幂等

来源页处理 onPop 结果时可能同时收到数据订阅刷新。结果消费者应按 revision 或操作编号判断是否已经应用,重复结果只触发一次界面更新。若返回的是稳定 ID,仍要从 Repository 读取当前版本,而不是直接把结果对象追加到列表。

对于一次性选择器,可以在成功消费后清理等待标记;对于编辑页,需要比较返回 revision 与本地最新 revision,旧结果不能覆盖更新的数据。这样即使生命周期回调顺序变化,状态也不会倒退。

三十、官方边界与验证声明

华为官方 Navigation 参考确认 pushDestination 通过 Promise 返回接口调用结果,支持动画或 NavigationOptions 重载,并列出相关路由错误码;页面路由指南说明 Push 类接口用于页面入栈。具体签名和行为以目标 SDK 为准。

本文未执行 HarmonyOS API 26 工程编译、系统 pushDestination 调用、错误码注入、onPop 回调联调、ArkUI 生命周期与转场验证或多设备真机验收,不将32项主机测试写成系统结果。

三十一、总结

可靠的异步入栈包含完整闭环:目标与参数先预检,相同请求合并,实例模式显式选择,Promise 错误分类,失败保留来源页,onPop 独立交付结果,迟到回调由 owner 代次隔离。把入栈结果、页面结果和业务结果分开,Navigation 才不会制造隐蔽竞态。

三十二、AI辅助声明

三十三、接入前检查清单

HarmonyOS AP正式接入前,工程侧要过一遍检查清单:SDK 版本与目标 API 版本匹配、权限在清单中声明且按需申请、能力开关在专用配置层管理、包体积增量符合预算。任何一项不过就先停,带着不匹配的版本往上冲,排错成本会翻倍。

interface PrecheckItem { name: string; pass: boolean; note?: string }
const checklist: PrecheckItem[] = [
  { name: 'SDK/API版本匹配', pass: false },
  { name: '权限声明完整', pass: false },
  { name: '开关集中管理', pass: false },
  { name: '包体积增量<预算', pass: false },
]
const allPass = checklist.every(x => x.pass)

检查清单跑通的标志是:新建工程按清单接入一次成功,且不依赖任何口头经验。做不到,说明文档还缺关键步骤。

三十四、常见坑与版本适配记录

HarmonyOS AP最常踩的坑集中在三处:示例代码用的旧接口在新版本被拆分、默认配置与生产预期不一致、调试环境与真机行为有差异。每踩一个坑就记进版本适配表,注明坑点、影响版本、修复方式与验证方式。

interface Pitfall { issue: string; sinceApi: string; fix: string; verifiedOn: string[] }
const pitfalls: Pitfall[] = []
function knownPit(api: string): Pitfall[] { return pitfalls.filter(p => p.sinceApi === api) }

适配表的价值在升级时兑现:每次目标版本变更,先对照表过一遍已知坑,能省下一整轮盲目排查。

Logo

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

更多推荐