HarmonyOS 7 实战 08:精准碰一碰,碰出应用新玩法
扫码点餐太慢了,碰一碰才是未来
去餐厅吃饭,现在普遍是扫码点餐。但扫码这事儿吧,有时候光线暗扫不出来,有时候角度不对扫半天。用户体验真的很一般。
HarmonyOS 推出了“精准碰一碰”,把手机往桌角的 NFC 标签上一贴,直接拉起应用,并且精准跳到“桌号 08”的点餐页面。这体验,比扫码优雅太多了。
但很多同学做这个功能时,卡在了第一步:“为什么我碰了没反应?”或者“为什么碰了只打开了首页,没有跳到指定页面?”
这篇就手把手教你实现精准碰一碰,从 NFC 配置到参数解析,坑都给你标出来了。
一、前提条件: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 传进来。
二、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');
}
这样无论应用是冷启动还是热启动,碰一碰都能正确响应。
四、避坑合集
坑一:碰了没反应。
检查三点:
-
标签里写的是不是 NDEF URI 格式(有些手环、门禁卡不是 NDEF 格式,读不出来)。
-
module.json5的skills有没有配置对应的uris。 -
系统的 NFC 开关有没有打开。
坑二:跳转了,但参数丢了。
want.uri 在某些情况下可能为空。如果为空,去检查 want.parameters,有时候系统会把 URI 放在 parameters 里。做好双重兜底。
坑三:应用冷启动时,页面还没准备好。
onCreate 里拿到 tableId 后,如果直接 router.pushUrl,可能会失败,因为页面栈还没初始化。这时候应该把数据存到 AppStorage,等首页加载完再读取。
坑四:NFC 标签被其他应用抢了。
如果你的 URI 和别的应用匹配规则重叠,系统会弹出一个选择框让用户选。为了避免这种情况,配置 uris 的时候尽量精确,host 和 path 都要写全。
五、总结
精准碰一碰的核心在于两点:NFC 标签的 NDEF 数据和应用的 skills 配置。核心记住三点:
-
重写
onNewWant,否则应用已打开时碰一碰没反应。 -
用 EventHub 或 AppStorage 传递参数,避免页面未初始化导致跳转失败。
-
module.json5的 uris 要配准,不然只会拉起首页。
更多推荐

所有评论(0)