HarmonyOS 6.0 NFC标签读写与碰一碰连接——物联网应用的近场入口
手机碰一碰 NFC 标签就弹出配置页、碰一碰配网 WiFi、碰一碰开门禁——NFC 是智能家居和 IoT 场景的关键入口。HarmonyOS NEXT 提供了完整的 NFC API:标签识别、NDEF 读写、前台分发优先注册。这篇把 NFC 近场通信的核心用法讲清楚。
NFC 通信概览
NFC(近场通信)的核心概念:
- Tag(标签)——NFC 贴纸、NFC 卡片等被动设备
- NDEF——NFC 数据交换格式,标准化的数据组织方式
- 前台分发——APP 在前台时优先接收 NFC 意图
- AAR——Android Application Record,碰一碰自动打开指定 APP
import{tag}from'@kit.ConnectivityKit'import{ndef}from'@kit.ConnectivityKit'权限配置
{ "requestPermissions": [ { "name": "ohos.permission.NFC_TAG" }, { "name": "ohos.permission.NFC_ADAPTER" } ] }标签检测
标签靠近手机时,系统通过 Ability 的 onNewWant 分发 NFC 意图。
// EntryAbility.etsonNewWant(want:Want,launchParam:AbilityConstant.LaunchParam):void{lettagInfo:tag.TagInfo|null=nulltry{tagInfo=tag.getTagInfo(want)}catch(e){return}if(tagInfo===null)return// 判断标签类型if(tag.isNdef(tagInfo)){letndefTag:ndef.NdefTag=ndef.getNdefTag(tagInfo)// 读取 NDEF 内容}}要点:getTagInfo 从 want 中提取标签信息,isNdef 判断是否为 NDEF 格式标签。非 NDEF 标签(如 MifareClassic)用对应的 getXXXTag 方法。
NDEF 读写
NDEF 是最常用的 NFC 数据格式——类似"JSON for NFC"。
读取 NDEF
letndefTag:ndef.NdefTag=ndef.getNdefTag(tagInfo)letndefMessage:ndef.NdefMessage=ndefTag.getNdefMessage()letrecords:ndef.NdefRecord[]=ndefMessage.getRecords()for(leti:number=0;i<records.length;i++){letrecord:ndef.NdefRecord=records[i]letpayload:string=record.payloadasstringletrecordType:string=record.recordTypeasstring// URI 类型: recordType = 'U'// 文本类型: recordType = 'T'}写入 NDEF
// 创建 NDEF 消息leturiRecord:ndef.NdefRecord=ndef.createUriRecord('https://harmonyos.com')letndefMsg:ndef.NdefMessage=ndef.createNdefMessage([uriRecord])// 写入标签ndefTag.writeNdef(ndefMsg)创建不同类型的 NDEF Record
// URI 记录leturi:ndef.NdefRecord=ndef.createUriRecord('https://example.com')// 文本记录lettext:ndef.NdefRecord=ndef.createTextRecord('zh','你好HarmonyOS')// MIME 记录letmime:ndef.NdefRecord=ndef.createMimeRecord('application/json','{"key":"value"}')// AAR 记录(碰一碰打开APP)letaar:ndef.NdefRecord=ndef.createAarRecord('com.example.myapp')要点:AAR 记录让手机碰标签时自动打开指定包名的 APP,实现"碰一碰直连"。
前台分发
当多个 APP 都能处理 NFC 标签时,前台分发让当前 APP 优先接收。
import{tag}from'@kit.ConnectivityKit'// 注册前台分发letelementName:ElementName={bundleName:'com.example.myapp',abilityName:'EntryAbility'}lettechList:string[][]=[['ndef','ndef-formatable']]tag.registerForegroundDispatch(elementName,techList)// 取消前台分发tag.unregisterForegroundDispatch(elementName)要点:registerForegroundDispatch 在 Ability 的 onForeground 注册,onBackground 取消。techList 指定感兴趣的标签技术类型。
NFC 碰一碰场景模拟 Demo
由于 NFC 需要真机 + NFC 标签,这里用模拟方式展示完整交互流程。
interfaceNfcRecord{id:stringtype:stringcontent:stringtime:string}@Entry@Componentstruct NfcSimDemo{@StatenfcRecords:NfcRecord[]=[]@StatetagDetected:boolean=false@StatecurrentTagType:string=''@StatendefContent:string=''@StatewriteInput:string='https://harmonyos.com'@StatestatusMsg:string='将NFC标签靠近手机...'build(){Column({space:16}){Text('NFC 碰一碰模拟').fontSize(22).fontWeight(FontWeight.Bold).width('100%')Column(){Column().width(100).height(100).borderRadius(50).backgroundColor(this.tagDetected?'#E8F5E9':'#F5F5F5').border({width:3,color:this.tagDetected?'#4CAF50':'#E0E0E0',style:BorderStyle.Dashed})Text(this.tagDetected?'标签已检测':'等待NFC标签').fontSize(14).fontColor(this.tagDetected?'#4CAF50':'#999999').margin({top:8})}.width('100%').alignItems(HorizontalAlign.Center).padding(16)Row({space:8}){Button('模拟标签靠近').onClick(()=>{this.tagDetected=truethis.currentTagType='NDEF'this.ndefContent='https://harmonyos.com'this.addRecord('检测','发现NDEF标签')this.statusMsg='标签已检测到(模拟)'})Button('模拟标签移开').onClick(()=>{this.tagDetected=falsethis.ndefContent=''this.statusMsg='标签已移开'this.addRecord('移开','标签离开')})}if(this.tagDetected){Column({space:8}){Text('标签信息').fontSize(16).fontWeight(FontWeight.Bold).width('100%')Row(){Text('类型:').fontSize(14).fontColor('#666666').width(60)Text(this.currentTagType).fontSize(14).fontWeight(FontWeight.Medium)}Row(){Text('NDEF:').fontSize(14).fontColor('#666666').width(60)Text(this.ndefContent||'(空)').fontSize(14).fontColor('#1a73e8')}TextInput({text:this.writeInput,placeholder:'写入内容'}).onChange((value:string)=>{this.writeInput=value})Row({space:8}){Button('写入NDEF').onClick(()=>{this.ndefContent=this.writeInputthis.addRecord('写入',this.writeInput)})Button('读取NDEF').onClick(()=>{this.addRecord('读取',this.ndefContent||'(空)')})Button('清空').onClick(()=>{this.ndefContent=''this.addRecord('清空','NDEF已清除')})}}.width('100%').padding(16).borderRadius(12).backgroundColor('#FFFFFF')}// 操作记录if(this.nfcRecords.length>0){Text('操作记录:').fontSize(16).fontWeight(FontWeight.Bold).width('100%')ForEach(this.nfcRecords.slice().reverse(),(record:NfcRecord)=>{Row({space:8}){Text(record.time).fontSize(11).fontColor('#999999').width(60)Text(record.type).fontSize(12).fontWeight(FontWeight.Medium).fontColor('#1a73e8').width(50)Text(record.content).fontSize(12).fontColor('#333333').layoutWeight(1)}.padding(4)},(record:NfcRecord)=>record.id)}Text(this.statusMsg).fontSize(12).fontColor('#999999')}.width('100%').padding(20)}privateaddRecord(type:string,content:string):void{this.nfcRecords.push({id:Date.now().toString(),type:type,content:content,time:newDate().toLocaleTimeString()})}}碰一碰配网实战
最常见的 IoT 场景——手机碰一碰设备上的 NFC 标签,自动跳转配网页面,输入 WiFi 密码后下发给设备。
// NDEF 标签中写入的内容// URI: https://myapp.com/setup?device=ABC123// AAR: com.example.iotapp// APP 收到 NFC 意图后解析onNewWant(want:Want,launchParam:AbilityConstant.LaunchParam):void{lettagInfo:tag.TagInfo|null=tag.getTagInfo(want)if(tagInfo===null)returnif(tag.isNdef(tagInfo)){letndefTag:ndef.NdefTag=ndef.getNdefTag(tagInfo)letmsg:ndef.NdefMessage=ndefTag.getNdefMessage()letrecords:ndef.NdefRecord[]=msg.getRecords()// 解析 URI 中的设备 ID// 跳转到配网页面leturi:string=records[0].payloadasstringletdeviceId:string=this.parseDeviceId(uri)router.pushUrl({url:'pages/WifiSetup',params:{deviceId:deviceId}})}}标签类型判断
不同标签支持不同的技术:
| 标签类型 | 判断方法 | 适用场景 |
|---|---|---|
| NDEF | tag.isNdef() | 通用数据读写 |
| MifareClassic | tag.isMifareClassic() | 门禁卡、公交卡 |
| MifareUltralight | tag.isMifareUltralight() | NFC 贴纸 |
| NfcA | tag.isNfcA() | ISO 14443-3A |
| NfcB | tag.isNfcB() | ISO 14443-3B |
| NfcF | tag.isNfcF() | FeliCa |
| NfcV | tag.isNfcV() | ISO 15693 |
要点:先判断标签类型,再用对应的 API 操作。NDEF 是最通用的格式,优先使用。
踩坑清单
| 问题 | 原因 | 解决 |
|---|---|---|
| getTagInfo 返回 null | want 不是 NFC 意图 | 检查 intent 来源 |
| isNdef 返回 false | 标签不是 NDEF 格式 | 空白标签先格式化 |
| writeNdef 报错 | 标签只读或容量不足 | 检查标签是否可写 |
| 前台分发不生效 | 未在 onForeground 注册 | onForeground 注册、onBackground 取消 |
| 碰标签打开别的 APP | 未注册前台分发 | 注册前台分发 + AAR 记录 |
| NDEF 解析乱码 | 编码处理不对 | URI 类型用 UTF-8,文本注意语言码 |
| readNdef 返回空 | 标签无 NDEF 消息 | 先检查 NDEF 消息是否存在 |
| 标签检测延迟 | 系统轮询间隔 | 前台分发 + 注册 techList |
| 多标签干扰 | 同时靠近多个标签 | 一次只靠近一个标签 |
| createUriRecord 格式错 | URI 不完整 | 必须含协议前缀 https:// |