扫码点餐太慢了,碰一碰才是未来

去餐厅吃饭,现在普遍是扫码点餐。但扫码这事儿吧,有时候光线暗扫不出来,有时候角度不对扫半天。用户体验真的很一般。

HarmonyOS 推出了“精准碰一碰”,把手机往桌角的 NFC 标签上一贴,直接拉起应用,并且精准跳到“桌号 08”的点餐页面。这体验,比扫码优雅太多了。

但很多同学做这个功能时,卡在了第一步:“为什么我碰了没反应?”或者“为什么碰了只打开了首页,没有跳到指定页面?”

这篇就手把手教你实现精准碰一碰,从 NFC 配置到参数解析,坑都给你标出来了。

https://placeholder-01.jpg/

一、前提条件:NFC 和 App Linking 缺一不可

碰一碰不是单机功能,它依赖两个核心能力:NFC 读取和应用拉起(App Linking)。

首先,你的应用必须在 module.json5 里声明 NFC 权限:

json

{
  "requestPermissions": [
    { "name": "ohos.permission.NFC_TAG" },
    { "name": "ohos.permission.NFC_CONTROLLER" }
  ]
}

NFC_TAG 是读取标签的权限,NFC_CONTROLLER 是控制 NFC 开关的。这两个都是 normal 级别,声明即可,不需要动态申请。

但光有权限还不够,你的应用还得能被系统“认领”。也就是说,当用户碰到一个 NFC 标签时,系统要知道该拉起哪个应用。这就需要在 module.json5 的 skills 里配置:

json

{
  "skills": [
    {
      "entities": ["entity.system.home"],
      "actions": ["ohos.want.action.home"]
    },
    {
      "actions": ["ohos.nfc.tag.action.TAG_FOUND"],
      "uris": [
        {
          "type": "vnd.android.nfc",
          "scheme": "app",
          "host": "com.example.myapp",
          "path": "menu"
        }
      ]
    }
  ]
}

这里的 uris 是关键。当 NFC 标签里写入的 URI 匹配到 com.example.myapp/menu 时,系统就会拉起你的应用,并把标签里的参数通过 want 传进来。

https://placeholder-02.png/

二、NFC 标签里到底写了什么

NFC 标签本身就是一个存储芯片,里面存了一段 NDEF 格式的数据。我们要做的,就是在标签里写入一个 URI,比如:

text

https://www.example.com/menu?tableId=08

但 HarmonyOS 的精准碰一碰,更推荐用 App Linking 的方式。

当手机碰触标签时,系统会读取 URI,然后匹配 module.json5 里的配置。如果匹配上了,系统会直接拉起应用,并把这个 URI 作为 want 的参数传进来。

在 UIAbility 的 onCreate 或 onNewWant 里,我们可以这样解析:

typescript

import Want from '@ohos.app.ability.Want';

export default class EntryAbility extends UIAbility {
  onCreate(want: Want, launchParam: AbilityConstant.LaunchParam) {
    this.handleNfcWant(want);
  }

  onNewWant(want: Want, launchParam: AbilityConstant.LaunchParam) {
    this.handleNfcWant(want);
  }

  private handleNfcWant(want: Want) {
    if (want.uri) {
      const uri = want.uri;
      console.info('NFC 拉起 URI: ' + uri);
      // 解析 tableId
      const tableId = this.parseTableId(uri);
      if (tableId) {
        // 跳转到点餐页面,并携带 tableId
        AppStorage.setOrCreate('tableId', tableId);
        // 这里可以路由到指定页面
      }
    }
  }

  private parseTableId(uri: string): string | null {
    const match = uri.match(/tableId=(\d+)/);
    return match ? match[1] : null;
  }
}

这里有个巨大的坑:onNewWant 一定要重写!

如果你只写了 onCreate,那么应用在后台已经打开的情况下,用户再碰一次 NFC 标签,应用根本不会有任何反应,因为它不会重新走 onCreate。只有重写了 onNewWant,才能处理这种“应用已启动”的情况。

三、前台分发:应用已经打开怎么办

如果应用已经在前台运行,用户碰一碰,系统会触发 onNewWant,这没问题。但如果用户正在应用里,碰一碰,我们希望直接刷新当前页面的数据,而不是重新走一遍启动流程。

这时候可以用 EventHub 或者 AppStorage 来通知页面。

在 Ability 里:

typescript

private handleNfcWant(want: Want) {
  if (want.uri) {
    const tableId = this.parseTableId(want.uri);
    if (tableId) {
      // 通过 EventHub 通知页面刷新
      this.context.eventHub.emit('nfc_table_changed', tableId);
    }
  }
}

在页面里监听:

typescript

aboutToAppear() {
  this.context.eventHub.on('nfc_table_changed', (tableId: string) => {
    this.tableId = tableId;
    this.loadMenu(tableId); // 重新加载菜单
  });
}

aboutToDisappear() {
  this.context.eventHub.off('nfc_table_changed');
}

这样无论应用是冷启动还是热启动,碰一碰都能正确响应。

https://placeholder-03.png/

四、避坑合集

坑一:碰了没反应。

检查三点:

  1. 标签里写的是不是 NDEF URI 格式(有些手环、门禁卡不是 NDEF 格式,读不出来)。

  2. module.json5 的 skills 有没有配置对应的 uris。

  3. 系统的 NFC 开关有没有打开。

坑二:跳转了,但参数丢了。

want.uri 在某些情况下可能为空。如果为空,去检查 want.parameters,有时候系统会把 URI 放在 parameters 里。做好双重兜底。

坑三:应用冷启动时,页面还没准备好。

onCreate 里拿到 tableId 后,如果直接 router.pushUrl,可能会失败,因为页面栈还没初始化。这时候应该把数据存到 AppStorage,等首页加载完再读取。

坑四:NFC 标签被其他应用抢了。

如果你的 URI 和别的应用匹配规则重叠,系统会弹出一个选择框让用户选。为了避免这种情况,配置 uris 的时候尽量精确,host 和 path 都要写全。

https://placeholder-04.jpg/

五、总结

精准碰一碰的核心在于两点:NFC 标签的 NDEF 数据和应用的 skills 配置。核心记住三点:

  1. 重写 onNewWant,否则应用已打开时碰一碰没反应。

  2. 用 EventHub 或 AppStorage 传递参数,避免页面未初始化导致跳转失败。

  3. module.json5 的 uris 要配准,不然只会拉起首页。

Logo

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

更多推荐