跳转到正文
YGJoon 技术博客

HarmonyOS 元服务深链路由:处理 Want 冷热启动与页面栈时序

发布于:
·1.3k 字 · 约 3 分钟·作者:
围绕 singleton UIAbility,串起 Want 参数解析、冷启动暂存、热启动 onNewWant 和 NavPathStack 就绪后的安全路由。
HarmonyOS 元服务深链路由:处理 Want 冷热启动与页面栈时序文章封面
目录

冷启动与热启动不是同一条回调路径

工程入口声明了 launchType: "singleton"。在这种单实例启动模式下,如果入口 UIAbility 已经存在,新的启动请求会进入 onNewWant;冷启动则先经过 onCreate,页面和 NavPathStack 随后才初始化。把路由代码只放在其中一个回调里,另一条路径就会丢失目标页面或直接拿不到已挂载的页面栈。

我在源码里看到的流程是:onCreate 暂存 Want 中的目标 ID,首页 aboutToAppear 附加 AppRouter 后读取并清空暂存值;onNewWant 则尝试直接进入目标页。这个骨架抓住了关键时序,但热启动时还应确认路由栈确实已准备好,并对外部参数做白名单校验。

先把入口过滤范围收窄

module.json5 中的 skills 决定哪些动作、实体和 URI 可以匹配 Ability。下面是匿名化的 HTTPS 过滤示例:

{
  "skills": [
    {
      "actions": ["ohos.want.action.viewData"],
      "entities": ["entity.system.browsable"],
      "uris": [
        {
          "scheme": "https",
          "host": "service.example.com",
          "pathStartWith": "service/detail"
        }
      ],
      "domainVerify": true
    }
  ]
}

pathStartWith 用于限制 URL 路径前缀;URI 筛选只决定候选 Ability 是否匹配,并不等于服务端数据授权。若按 App Linking 使用 HTTPS 链接,官方要求目标 Ability 的 actions、entities、https URI 与域名验证配置符合规则,还需完成相应的平台域名关联流程。当前工程快照的 URI 声明把 domainVerify 设为 false,所以不能将它描述成已经验证的 App Linking。

两条启动路径汇入一个“待处理目标”

冷启动收到目标时页面栈尚不存在,先将经过校验的目标写入暂存槽;热启动收到目标时,如果路由器已经附加,就直接导航,否则也走暂存。页面初始化只消费一次,并在导航前清空,避免页面重建时重复打开。

以下片段展示入口侧的分流。示例只接受简单的资源键;生产代码还应限制长度、字符集和允许访问的目标范围。

import { AbilityConstant, UIAbility, Want } from '@kit.AbilityKit';
import Url from '@ohos.url';
import { AppRouter } from '../router/AppRouter';

const PENDING_RESOURCE_KEY: string = 'pendingResourceId';

export default class EntryAbility extends UIAbility {
  onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
    AppStorage.setOrCreate<string>(PENDING_RESOURCE_KEY, this.readResourceId(want));
  }

  onNewWant(want: Want, launchParam: AbilityConstant.LaunchParam): void {
    const resourceId: string = this.readResourceId(want);
    if (resourceId.length === 0) {
      return;
    }

    if (AppRouter.getStack() === undefined) {
      AppStorage.setOrCreate<string>(PENDING_RESOURCE_KEY, resourceId);
      return;
    }

    AppRouter.openResource(resourceId);
  }

  private readResourceId(want: Want): string {
    const value: Object | undefined = want.parameters?.['resourceId'];
    let resourceId: string = typeof value === 'string' ? value : '';

    if (resourceId.length === 0 && want.uri !== undefined && want.uri.length > 0) {
      try {
        const parsedUrl = Url.URL.parseURL(want.uri);
        resourceId = parsedUrl.params.get('resourceId') ?? '';
      } catch (error) {
        return '';
      }
    }

    if (resourceId.length === 0 || resourceId.length > 64 || !/^[A-Za-z0-9-]+$/.test(resourceId)) {
      return '';
    }
    return resourceId;
  }
}

这里把同一个参数读取函数用于 onCreate 和 onNewWant:Want 的结构化参数优先,URI query 由 HarmonyOS URL API 解析。两条入口最终都经过长度和字符集校验,避免冷、热启动对参数规则理解不一致。不能把 URL 原文直接拼成页面路由,也不能仅靠 skills 匹配就信任传入值。

等导航容器就绪后再消费

首页完成路由器绑定后读取暂存参数,先清空,再压入目标页:

aboutToAppear(): void {
  AppRouter.attach(this.pageStack);

  const resourceId: string = AppStorage.get<string>(PENDING_RESOURCE_KEY) ?? '';
  if (resourceId.length === 0) {
    return;
  }

  AppStorage.setOrCreate<string>(PENDING_RESOURCE_KEY, '');
  AppRouter.openResource(resourceId);
}

AppRouter.openResource 应是项目内唯一的“外部目标到页面栈”入口:检查目标类型、构造路由参数,并决定是复用当前详情页、替换栈顶还是新建页面。若每次都无条件 push,用户连续点开相同链接时会堆出一串重复页面;如果产品预期返回首页,则应在这个封装里先清理或替换页面栈。

路由之外的两道边界

参考资料

上一篇:HarmonyOS 元服务卡片:从空生命周期到可更新的数据链路 · 回看:元服务配置核查。


分享这篇文章:


相关文章

上一篇
HarmonyOS 元服务卡片:从空生命周期到可更新的数据链路