HarmonyOS APP开发NFC标签读写:NdefMessage处理
【摘要】 HarmonyOS APP开发NFC标签读写:NdefMessage处理NFC(近场通信)技术已经渗透到我们生活的方方面面——刷地铁卡、门禁卡、手机支付,甚至产品标签扫码。今天咱们就来聊聊鸿蒙系统中的NFC标签读写,特别是NDEF消息的处理,看看如何让你的应用具备"碰一碰"的能力。 一、背景与动机 1.1 NFC技术概述NFC(Near Field Communication)是一种短距离...
HarmonyOS APP开发NFC标签读写:NdefMessage处理
NFC(近场通信)技术已经渗透到我们生活的方方面面——刷地铁卡、门禁卡、手机支付,甚至产品标签扫码。今天咱们就来聊聊鸿蒙系统中的NFC标签读写,特别是NDEF消息的处理,看看如何让你的应用具备"碰一碰"的能力。
一、背景与动机
1.1 NFC技术概述
NFC(Near Field Communication)是一种短距离高频无线通信技术,工作在13.56MHz频段:
| 对比项 | NFC | 蓝牙 | WiFi |
|---|---|---|---|
| 通信距离 | < 10cm | ~10m | ~100m |
| 建立时间 | < 0.1s | 数秒 | 数秒 |
| 功耗 | 极低 | 中等 | 较高 |
| 安全性 | 高(距离近) | 中 | 中 |
| 典型应用 | 支付、门禁、标签 | 音频、文件 | 网络接入 |
NFC的三种工作模式:
- 读写模式(Reader/Writer):手机读取或写入NFC标签
- 点对点模式(P2P):两台NFC设备间数据传输
- 卡模拟模式(Card Emulation):手机模拟成NFC卡片
本文重点讲解读写模式。
1.2 NDEF消息格式
NDEF(NFC Data Exchange Format)是NFC论坛定义的标准数据格式:

常见NDEF记录类型:
| 类型 | MIME类型 | 用途 |
|---|---|---|
| Text | text/plain | 文本信息 |
| URI | URI | 网址、电话等 |
| Smart Poster | smartposter | 智能海报(包含URI和文本) |
| MIME | 自定义 | 任意数据 |
| AAR | application/vnd.android.package-archive | Android应用启动 |
二、核心原理
2.1 NFC标签读写流程

2.2 标签类型与技术
NFC标签有多种类型,支持不同的技术:
// 标签类型
enum TagType {
// NDEF格式标签
NDEF = 'NdefTag',
// Mifare系列
MIFARE_CLASSIC = 'MifareClassicTag',
MIFARE_ULTRALIGHT = 'MifareUltralightTag',
// ISO标准
ISO15693 = 'Iso15693Tag',
ISO7816 = 'Iso7816Tag',
// 其他
NFC_A = 'NfcATag',
NFC_B = 'NfcBTag',
NFC_F = 'NfcFTag',
NFC_V = 'NfcVTag'
}
// 标签技术能力
interface TagTechnology {
// 是否支持NDEF
isNdefSupported: boolean;
// 是否可写
isWritable: boolean;
// 存储大小
maxSize: number;
// 已用大小
usedSize: number;
}
2.3 NDEF记录结构详解
// TNF(Type Name Format)类型
enum Tnf {
EMPTY = 0x00, // 空记录
WELL_KNOWN = 0x01, // 已知类型(如URI、Text)
MIME_MEDIA = 0x02, // MIME类型
ABSOLUTE_URI = 0x03, // 绝对URI
EXTERNAL = 0x04, // 外部类型
UNKNOWN = 0x05, // 未知类型
UNCHANGED = 0x06, // 未改变(分块记录)
RESERVED = 0x07 // 保留
}
// NDEF记录
interface NdefRecord {
// 类型名称格式
tnf: Tnf;
// 类型
type: ArrayBuffer;
// 负载数据
payload: ArrayBuffer;
// 标识符(可选)
id?: ArrayBuffer;
}
// NDEF消息
interface NdefMessage {
// 记录数组
records: NdefRecord[];
}
三、代码实战
3.1 NFC管理核心类封装
import nfc from '@ohos.nfc';
import { BusinessError } from '@ohos.base';
/**
* NDEF记录工具类
* 用于创建和解析NDEF记录
*/
export class NdefHelper {
/**
* 创建文本记录
* @param text 文本内容
* @param languageCode 语言代码,默认'en'
*/
public static createTextRecord(text: string, languageCode: string = 'en'): nfc.NdefRecord {
// 准备语言代码字节
const langBytes = new TextEncoder().encode(languageCode);
const textBytes = new TextEncoder().encode(text);
// 构建payload: [语言代码长度 | 语言代码 | 文本]
const payload = new ArrayBuffer(1 + langBytes.length + textBytes.length);
const view = new Uint8Array(payload);
view[0] = langBytes.length; // 语言代码长度
view.set(langBytes, 1); // 语言代码
view.set(textBytes, 1 + langBytes.length); // 文本
return {
tnf: nfc.Tnf.WELL_KNOWN,
type: new TextEncoder().encode('T'), // 'T'表示文本类型
payload: payload
};
}
/**
* 解析文本记录
*/
public static parseTextRecord(record: nfc.NdefRecord): string {
if (record.tnf !== nfc.Tnf.WELL_KNOWN) {
throw new Error('不是文本记录');
}
const typeStr = new TextDecoder().decode(record.type);
if (typeStr !== 'T') {
throw new Error('不是文本记录');
}
const payload = new Uint8Array(record.payload);
const langLength = payload[0];
// 跳过语言代码,读取文本
const textBytes = payload.slice(1 + langLength);
return new TextDecoder().decode(textBytes);
}
/**
* 创建URI记录
* @param uri URI地址
*/
public static createUriRecord(uri: string): nfc.NdefRecord {
// URI前缀缩写表
const prefixes = [
'', // 0x00: 无前缀
'http://www.', // 0x01
'https://www.', // 0x02
'http://', // 0x03
'https://', // 0x04
'tel:', // 0x05
'mailto:', // 0x06
'ftp://anonymous:anonymous@', // 0x07
'ftp://ftp.', // 0x08
'ftps://', // 0x09
'sftp://', // 0x0A
'smb://', // 0x0B
'nfs://', // 0x0C
'ftp://', // 0x0D
'dav://', // 0x0E
'news:', // 0x0F
'telnet://', // 0x10
'imap:', // 0x11
'rtsp://', // 0x12
'urn:', // 0x13
'pop:', // 0x14
'sip:', // 0x15
'sips:', // 0x16
'tftp:', // 0x17
'btspp://', // 0x18
'btl2cap://', // 0x19
'btgoep://', // 0x1A
'tcpobex://', // 0x1B
'irdaobex://', // 0x1C
'file://', // 0x1D
'urn:epc:id:', // 0x1E
'urn:epc:tag:', // 0x1F
'urn:epc:pat:', // 0x20
'urn:epc:sct:', // 0x21
'urn:epc:raw:', // 0x22
'urn:epc:', // 0x23
'urn:nfc:' // 0x24
];
// 查找匹配的前缀
let prefixCode = 0;
let remainingUri = uri;
for (let i = prefixes.length - 1; i > 0; i--) {
if (uri.startsWith(prefixes[i])) {
prefixCode = i;
remainingUri = uri.substring(prefixes[i].length);
break;
}
}
// 构建payload: [前缀代码 | 剩余URI]
const uriBytes = new TextEncoder().encode(remainingUri);
const payload = new ArrayBuffer(1 + uriBytes.length);
const view = new Uint8Array(payload);
view[0] = prefixCode;
view.set(uriBytes, 1);
return {
tnf: nfc.Tnf.WELL_KNOWN,
type: new TextEncoder().encode('U'), // 'U'表示URI类型
payload: payload
};
}
/**
* 解析URI记录
*/
public static parseUriRecord(record: nfc.NdefRecord): string {
if (record.tnf !== nfc.Tnf.WELL_KNOWN) {
throw new Error('不是URI记录');
}
const typeStr = new TextDecoder().decode(record.type);
if (typeStr !== 'U') {
throw new Error('不是URI记录');
}
const prefixes = [
'', 'http://www.', 'https://www.', 'http://', 'https://',
'tel:', 'mailto:', 'ftp://anonymous:anonymous@', 'ftp://ftp.',
'ftps://', 'sftp://', 'smb://', 'nfs://', 'ftp://', 'dav://',
'news:', 'telnet://', 'imap:', 'rtsp://', 'urn:', 'pop:',
'sip:', 'sips:', 'tftp:', 'btspp://', 'btl2cap://', 'btgoep://',
'tcpobex://', 'irdaobex://', 'file://', 'urn:epc:id:',
'urn:epc:tag:', 'urn:epc:pat:', 'urn:epc:sct:', 'urn:epc:raw:',
'urn:epc:', 'urn:nfc:'
];
const payload = new Uint8Array(record.payload);
const prefixCode = payload[0];
const remainingUri = new TextDecoder().decode(payload.slice(1));
return (prefixes[prefixCode] || '') + remainingUri;
}
/**
* 创建MIME类型记录
*/
public static createMimeRecord(mimeType: string, data: ArrayBuffer): nfc.NdefRecord {
return {
tnf: nfc.Tnf.MIME_MEDIA,
type: new TextEncoder().encode(mimeType),
payload: data
};
}
/**
* 创建外部类型记录
*/
public static createExternalRecord(domain: string, type: string, data: ArrayBuffer): nfc.NdefRecord {
const externalType = `${domain}:${type}`;
return {
tnf: nfc.Tnf.EXTERNAL,
type: new TextEncoder().encode(externalType),
payload: data
};
}
/**
* 创建AAR记录(Android应用启动)
*/
public static createAarRecord(packageName: string): nfc.NdefRecord {
return {
tnf: nfc.Tnf.EXTERNAL,
type: new TextEncoder().encode('android.com:pkg'),
payload: new TextEncoder().encode(packageName)
};
}
}
/**
* NFC标签管理器
* 封装标签发现、读写等功能
*/
export class NfcTagManager {
private static instance: NfcTagManager;
// 当前检测到的标签
private currentTag: nfc.TagInfo | null = null;
// 回调函数
private onTagDiscovered: ((tag: nfc.TagInfo) => void) | null = null;
private constructor() {
this.initEventListeners();
}
/**
* 获取单例实例
*/
public static getInstance(): NfcTagManager {
if (!NfcTagManager.instance) {
NfcTagManager.instance = new NfcTagManager();
}
return NfcTagManager.instance;
}
/**
* 初始化事件监听
*/
private initEventListeners(): void {
// 监听标签发现
nfc.on('tagDiscovered', (tag: nfc.TagInfo) => {
console.info(`[NFC] 发现标签: ${tag.technology}`);
this.currentTag = tag;
if (this.onTagDiscovered) {
this.onTagDiscovered(tag);
}
});
}
/**
* 检查NFC是否开启
*/
public async isNfcEnabled(): Promise<boolean> {
try {
const isOpen = nfc.isNfcOpen();
return isOpen;
} catch (error) {
console.error('[NFC] 检查状态失败:', error);
return false;
}
}
/**
* 开启NFC
*/
public async enableNfc(): Promise<boolean> {
try {
await nfc.openNfc();
console.info('[NFC] 已开启');
return true;
} catch (error) {
const err = error as BusinessError;
console.error(`[NFC] 开启失败: ${err.message}`);
return false;
}
}
/**
* 关闭NFC
*/
public async disableNfc(): Promise<boolean> {
try {
await nfc.closeNfc();
console.info('[NFC] 已关闭');
return true;
} catch (error) {
const err = error as BusinessError;
console.error(`[NFC] 关闭失败: ${err.message}`);
return false;
}
}
/**
* 读取NDEF消息
*/
public async readNdefMessage(): Promise<nfc.NdefMessage | null> {
if (!this.currentTag) {
console.error('[NFC] 未检测到标签');
return null;
}
try {
// 获取NDEF标签技术
const ndefTag = nfc.getNdefTag(this.currentTag);
if (!ndefTag) {
console.error('[NFC] 标签不支持NDEF');
return null;
}
// 读取NDEF消息
const message = await ndefTag.readNdefMessage();
console.info(`[NFC] 读取成功,${message.records.length} 条记录`);
return message;
} catch (error) {
console.error('[NFC] 读取失败:', error);
return null;
}
}
/**
* 写入NDEF消息
*/
public async writeNdefMessage(message: nfc.NdefMessage): Promise<boolean> {
if (!this.currentTag) {
console.error('[NFC] 未检测到标签');
return false;
}
try {
// 获取NDEF标签技术
const ndefTag = nfc.getNdefTag(this.currentTag);
if (!ndefTag) {
console.error('[NFC] 标签不支持NDEF');
return false;
}
// 检查是否可写
const isWritable = await ndefTag.isNdefWritable();
if (!isWritable) {
console.error('[NFC] 标签不可写');
return false;
}
// 写入NDEF消息
await ndefTag.writeNdefMessage(message);
console.info('[NFC] 写入成功');
return true;
} catch (error) {
console.error('[NFC] 写入失败:', error);
return false;
}
}
/**
* 格式化标签(将标签格式化为NDEF)
*/
public async formatTag(): Promise<boolean> {
if (!this.currentTag) {
console.error('[NFC] 未检测到标签');
return false;
}
try {
const ndefTag = nfc.getNdefTag(this.currentTag);
if (!ndefTag) {
return false;
}
await ndefTag.formatNdef();
console.info('[NFC] 格式化成功');
return true;
} catch (error) {
console.error('[NFC] 格式化失败:', error);
return false;
}
}
/**
* 获取标签信息
*/
public getTagInfo(): nfc.TagInfo | null {
return this.currentTag;
}
/**
* 解析NDEF消息内容
*/
public parseNdefMessage(message: nfc.NdefMessage): Array<{
type: string;
content: any;
}> {
const results: Array<{ type: string; content: any }> = [];
message.records.forEach((record) => {
try {
if (record.tnf === nfc.Tnf.WELL_KNOWN) {
const typeStr = new TextDecoder().decode(record.type);
if (typeStr === 'T') {
// 文本记录
results.push({
type: 'text',
content: NdefHelper.parseTextRecord(record)
});
} else if (typeStr === 'U') {
// URI记录
results.push({
type: 'uri',
content: NdefHelper.parseUriRecord(record)
});
}
} else if (record.tnf === nfc.Tnf.MIME_MEDIA) {
// MIME类型记录
const mimeType = new TextDecoder().decode(record.type);
results.push({
type: 'mime',
content: {
mimeType: mimeType,
data: record.payload
}
});
} else if (record.tnf === nfc.Tnf.EXTERNAL) {
// 外部类型记录
const externalType = new TextDecoder().decode(record.type);
results.push({
type: 'external',
content: {
externalType: externalType,
data: record.payload
}
});
}
} catch (error) {
console.warn('[NFC] 解析记录失败:', error);
}
});
return results;
}
/**
* 设置标签发现回调
*/
public setOnTagDiscovered(callback: (tag: nfc.TagInfo) => void): void {
this.onTagDiscovered = callback;
}
/**
* 移除监听
*/
public removeAllListeners(): void {
nfc.off('tagDiscovered');
this.currentTag = null;
console.info('[NFC] 已移除监听');
}
}
3.2 NFC标签读写UI实现
import { NfcTagManager, NdefHelper } from './NfcTagManager';
import nfc from '@ohos.nfc';
@Entry
@Component
struct NfcReadWritePage {
private nfcManager: NfcTagManager = NfcTagManager.getInstance();
@State isNfcEnabled: boolean = false;
@State hasTag: boolean = false;
@State tagInfo: string = '';
@State ndefRecords: Array<{ type: string; content: any }> = [];
@State writeText: string = '';
@State writeUri: string = '';
@State isReading: boolean = false;
@State isWriting: boolean = false;
aboutToAppear(): void {
this.checkNfcState();
this.registerCallbacks();
}
aboutToDisappear(): void {
this.nfcManager.removeAllListeners();
}
/**
* 检查NFC状态
*/
async checkNfcState(): Promise<void> {
this.isNfcEnabled = await this.nfcManager.isNfcEnabled();
}
/**
* 注册回调
*/
registerCallbacks(): void {
this.nfcManager.setOnTagDiscovered((tag: nfc.TagInfo) => {
this.hasTag = true;
this.tagInfo = `技术: ${tag.technology}\nID: ${this.formatTagId(tag.uid)}`;
// 自动读取
this.readTag();
});
}
/**
* 格式化标签ID
*/
formatTagId(uid: ArrayBuffer): string {
if (!uid) return 'Unknown';
const bytes = new Uint8Array(uid);
return Array.from(bytes).map(b => b.toString(16).padStart(2, '0')).join(':').toUpperCase();
}
/**
* 读取标签
*/
async readTag(): Promise<void> {
this.isReading = true;
const message = await this.nfcManager.readNdefMessage();
if (message) {
this.ndefRecords = this.nfcManager.parseNdefMessage(message);
} else {
this.ndefRecords = [];
}
this.isReading = false;
}
/**
* 写入文本
*/
async writeTextToTag(): Promise<void> {
if (!this.writeText) return;
this.isWriting = true;
const record = NdefHelper.createTextRecord(this.writeText, 'zh');
const message: nfc.NdefMessage = {
records: [record]
};
const success = await this.nfcManager.writeNdefMessage(message);
if (success) {
this.writeText = '';
await this.readTag();
}
this.isWriting = false;
}
/**
* 写入URI
*/
async writeUriToTag(): Promise<void> {
if (!this.writeUri) return;
this.isWriting = true;
const record = NdefHelper.createUriRecord(this.writeUri);
const message: nfc.NdefMessage = {
records: [record]
};
const success = await this.nfcManager.writeNdefMessage(message);
if (success) {
this.writeUri = '';
await this.readTag();
}
this.isWriting = false;
}
/**
* 清空标签
*/
async clearTag(): Promise<void> {
this.isWriting = true;
const message: nfc.NdefMessage = {
records: []
};
await this.nfcManager.writeNdefMessage(message);
await this.readTag();
this.isWriting = false;
}
build() {
Column() {
// 标题栏
Row() {
Text('NFC标签读写')
.fontSize(24)
.fontWeight(FontWeight.Bold)
.fontColor('#FFFFFF')
Blank()
Toggle({ type: ToggleType.Switch, isOn: this.isNfcEnabled })
.onChange((isOn: boolean) => {
if (isOn) {
this.nfcManager.enableNfc();
} else {
this.nfcManager.disableNfc();
}
this.isNfcEnabled = isOn;
})
}
.width('100%')
.padding(20)
.backgroundColor('#1A1A2E')
if (this.isNfcEnabled) {
// 标签信息
if (this.hasTag) {
Column() {
Text('标签信息')
.fontSize(16)
.fontColor('#FFFFFF')
.width('100%')
.margin({ bottom: 10 })
Text(this.tagInfo)
.fontSize(14)
.fontColor('#AAAAAA')
.width('100%')
}
.width('90%')
.padding(15)
.backgroundColor('#16213E')
.borderRadius(12)
.margin({ top: 20 })
// 读取结果
Column() {
Row() {
Text('NDEF记录')
.fontSize(16)
.fontColor('#FFFFFF')
Blank()
Button('刷新')
.fontSize(12)
.height(30)
.backgroundColor('#4A90E2')
.enabled(!this.isReading)
.onClick(() => {
this.readTag();
})
}
.width('100%')
.margin({ bottom: 10 })
if (this.isReading) {
Row() {
LoadingProgress()
.width(30)
.height(30)
Text('读取中...')
.fontSize(14)
.fontColor('#AAAAAA')
.margin({ left: 10 })
}
.width('100%')
.justifyContent(FlexAlign.Center)
.padding(20)
} else if (this.ndefRecords.length > 0) {
ForEach(this.ndefRecords, (record: { type: string; content: any }, index: number) => {
Row() {
Text(`[${record.type}]`)
.fontSize(12)
.fontColor('#F5A623')
if (record.type === 'text' || record.type === 'uri') {
Text(record.content)
.fontSize(14)
.fontColor('#FFFFFF')
.margin({ left: 10 })
.layoutWeight(1)
} else {
Text(JSON.stringify(record.content))
.fontSize(12)
.fontColor('#AAAAAA')
.margin({ left: 10 })
.layoutWeight(1)
}
}
.width('100%')
.padding(10)
.backgroundColor('#1A1A2E')
.borderRadius(8)
.margin({ bottom: 8 })
})
} else {
Text('无NDEF记录')
.fontSize(14)
.fontColor('#AAAAAA')
.width('100%')
.textAlign(TextAlign.Center)
.padding(20)
}
}
.width('90%')
.padding(15)
.backgroundColor('#16213E')
.borderRadius(12)
.margin({ top: 20 })
// 写入区域
Column() {
Text('写入数据')
.fontSize(16)
.fontColor('#FFFFFF')
.width('100%')
.margin({ bottom: 15 })
// 写入文本
Row() {
TextInput({ text: $$this.writeText, placeholder: '输入文本' })
.width('70%')
.height(40)
.backgroundColor('#1A1A2E')
.fontColor('#FFFFFF')
Button('写入')
.width('25%')
.height(40)
.backgroundColor('#4CAF50')
.enabled(!this.isWriting && this.writeText.length > 0)
.onClick(() => {
this.writeTextToTag();
})
}
.width('100%')
.justifyContent(FlexAlign.SpaceBetween)
.margin({ bottom: 10 })
// 写入URI
Row() {
TextInput({ text: $$this.writeUri, placeholder: '输入网址' })
.width('70%')
.height(40)
.backgroundColor('#1A1A2E')
.fontColor('#FFFFFF')
Button('写入')
.width('25%')
.height(40)
.backgroundColor('#4A90E2')
.enabled(!this.isWriting && this.writeUri.length > 0)
.onClick(() => {
this.writeUriToTag();
})
}
.width('100%')
.justifyContent(FlexAlign.SpaceBetween)
.margin({ bottom: 15 })
// 清空按钮
Button('清空标签')
.width('100%')
.height(40)
.backgroundColor('#FF5722')
.enabled(!this.isWriting)
.onClick(() => {
this.clearTag();
})
}
.width('90%')
.padding(15)
.backgroundColor('#16213E')
.borderRadius(12)
.margin({ top: 20 })
} else {
// 等待标签
Column() {
Text('请将NFC标签靠近设备')
.fontSize(18)
.fontColor('#AAAAAA')
LoadingProgress()
.width(50)
.height(50)
.margin({ top: 20 })
}
.margin({ top: 100 })
}
} else {
// NFC未开启
Column() {
Text('NFC未开启')
.fontSize(18)
.fontColor('#AAAAAA')
Text('请开启NFC以读写标签')
.fontSize(14)
.fontColor('#666666')
.margin({ top: 10 })
}
.margin({ top: 100 })
}
}
.width('100%')
.height('100%')
.backgroundColor('#0F0F1A')
}
}
3.3 智能海报示例
智能海报(Smart Poster)是一种复合NDEF记录,可以同时包含URI、文本、图标等信息:
import { NdefHelper } from './NdefHelper';
import nfc from '@ohos.nfc';
/**
* 智能海报构建器
*/
export class SmartPosterBuilder {
private records: nfc.NdefRecord[] = [];
private mainUri: string = '';
/**
* 设置主URI
*/
public setUri(uri: string): SmartPosterBuilder {
this.mainUri = uri;
this.records.push(NdefHelper.createUriRecord(uri));
return this;
}
/**
* 添加标题
*/
public addTitle(title: string, language: string = 'en'): SmartPosterBuilder {
this.records.push(NdefHelper.createTextRecord(title, language));
return this;
}
/**
* 添加图标(MIME类型)
*/
public addIcon(imageData: ArrayBuffer, mimeType: string = 'image/png'): SmartPosterBuilder {
this.records.push(NdefHelper.createMimeRecord(mimeType, imageData));
return this;
}
/**
* 添加动作
* 0: 执行动作(如打开应用)
* 1: 保存动作(如保存联系人)
* 2: 编辑动作
*/
public addAction(action: number): SmartPosterBuilder {
const payload = new ArrayBuffer(1);
new Uint8Array(payload)[0] = action;
this.records.push({
tnf: nfc.Tnf.WELL_KNOWN,
type: new TextEncoder().encode('act'),
payload: payload
});
return this;
}
/**
* 构建智能海报NDEF消息
*/
public build(): nfc.NdefMessage {
// 智能海报是一个复合记录,包含多个子记录
const spPayload = this.encodeRecords(this.records);
const spRecord: nfc.NdefRecord = {
tnf: nfc.Tnf.WELL_KNOWN,
type: new TextEncoder().encode('Sp'), // 'Sp'表示Smart Poster
payload: spPayload
};
return {
records: [spRecord]
};
}
/**
* 编码多个记录为一个payload
*/
private encodeRecords(records: nfc.NdefRecord[]): ArrayBuffer {
// 简化实现,实际需要按照NDEF规范编码
// 这里只是示意
let totalLength = 0;
records.forEach(r => {
totalLength += this.calculateRecordSize(r);
});
const buffer = new ArrayBuffer(totalLength);
// 实际编码逻辑...
return buffer;
}
private calculateRecordSize(record: nfc.NdefRecord): number {
return 3 + record.type.byteLength +
(record.id ? record.id.byteLength : 0) +
record.payload.byteLength;
}
}
// 使用示例
async function createSmartPoster() {
const message = new SmartPosterBuilder()
.setUri('https://www.example.com/product/123')
.addTitle('特价商品', 'zh')
.addTitle('Special Offer', 'en')
.addAction(0) // 打开网页
.build();
await nfcManager.writeNdefMessage(message);
}
四、踩坑与注意事项
4.1 权限配置
// module.json5
{
"module": {
"requestPermissions": [
{
"name": "ohos.permission.NFC",
"reason": "使用NFC功能"
},
{
"name": "ohos.permission.NFC_TAG",
"reason": "读写NFC标签"
}
]
}
}
4.2 前台调度
坑点:NFC检测需要应用在前台。
/**
* 注册前台NFC调度
*/
async function registerForegroundDispatch(): Promise<void> {
// 在Ability的onForeground中调用
try {
await nfc.enableForegroundDispatch();
console.info('[NFC] 前台调度已启用');
} catch (error) {
console.error('[NFC] 启用前台调度失败:', error);
}
}
/**
* 取消前台NFC调度
*/
async function disableForegroundDispatch(): Promise<void> {
// 在Ability的onBackground中调用
try {
await nfc.disableForegroundDispatch();
console.info('[NFC] 前台调度已禁用');
} catch (error) {
console.error('[NFC] 禁用前台调度失败:', error);
}
}
4.3 标签类型判断
坑点:不同标签支持的技术不同。
/**
* 检查标签能力
*/
async function checkTagCapabilities(tag: nfc.TagInfo): Promise<{
canRead: boolean;
canWrite: boolean;
canFormat: boolean;
}> {
const result = {
canRead: false,
canWrite: false,
canFormat: false
};
// 检查是否支持NDEF
const ndefTag = nfc.getNdefTag(tag);
if (ndefTag) {
result.canRead = true;
try {
const isWritable = await ndefTag.isNdefWritable();
result.canWrite = isWritable;
} catch (error) {
result.canWrite = false;
}
}
// 检查是否可格式化
const ndefFormatable = nfc.getNdefFormatableTag(tag);
result.canFormat = !!ndefFormatable;
return result;
}
4.4 写入容量检查
坑点:标签容量有限,写入前需要检查。
/**
* 检查消息大小是否适合标签
*/
async function checkMessageSize(message: nfc.NdefMessage): Promise<boolean> {
const tag = nfcManager.getTagInfo();
if (!tag) return false;
const ndefTag = nfc.getNdefTag(tag);
if (!ndefTag) return false;
try {
const maxSize = await ndefTag.getMaxNdefMessageSize();
const messageSize = calculateMessageSize(message);
if (messageSize > maxSize) {
console.error(`[NFC] 消息大小 ${messageSize} 超过标签容量 ${maxSize}`);
return false;
}
return true;
} catch (error) {
return false;
}
}
/**
* 计算NDEF消息大小
*/
function calculateMessageSize(message: nfc.NdefMessage): number {
let size = 0;
message.records.forEach((record) => {
// NDEF记录头 + 类型 + ID + 负载
size += 3; // 头部
size += record.type.byteLength;
if (record.id) size += record.id.byteLength;
size += record.payload.byteLength;
});
return size;
}
4.5 数据编码问题
坑点:文本编码需要指定语言和编码方式。
/**
* 正确的文本记录创建
*/
function createTextRecordProper(text: string, language: string = 'zh'): nfc.NdefRecord {
const langBytes = new TextEncoder().encode(language);
const textBytes = new TextEncoder().encode(text);
// Payload格式: [状态字节 | 语言代码 | 文本]
// 状态字节: bit7=编码(0=UTF-8,1=UTF-16), bit6=保留, bit5-0=语言代码长度
const statusByte = langBytes.length & 0x3F; // UTF-8编码
const payload = new ArrayBuffer(1 + langBytes.length + textBytes.length);
const view = new Uint8Array(payload);
view[0] = statusByte;
view.set(langBytes, 1);
view.set(textBytes, 1 + langBytes.length);
return {
tnf: nfc.Tnf.WELL_KNOWN,
type: new TextEncoder().encode('T'),
payload: payload
};
}
五、HarmonyOS 6适配
5.1 API变更
| API | HarmonyOS 5 | HarmonyOS 6 | 说明 |
|---|---|---|---|
| readNdefMessage() | 同步返回 | 异步Promise | 改为异步 |
| writeNdefMessage() | 基础功能 | 支持覆盖写入 | 新增覆盖选项 |
| 新增 | - | getTagTechnologyList() | 获取支持的技术列表 |
适配代码:
/**
* HarmonyOS 6 NFC适配
*/
async function readNdefAdaptive(): Promise<nfc.NdefMessage | null> {
const apiVersion = getApiVersion();
const tag = nfcManager.getTagInfo();
if (!tag) return null;
try {
const ndefTag = nfc.getNdefTag(tag);
if (!ndefTag) return null;
if (apiVersion >= 6) {
// HarmonyOS 6
return await ndefTag.readNdefMessage();
} else {
// HarmonyOS 5
return ndefTag.readNdefMessage();
}
} catch (error) {
console.error('[NFC] 读取失败:', error);
return null;
}
}
5.2 新增功能
HarmonyOS 6增强了NFC能力:
// 1. 获取标签支持的所有技术
const technologies = await nfc.getTagTechnologyList(tag);
console.info(`支持的技术: ${technologies.join(', ')}`);
// 2. NDEF消息缓存
const cachedMessage = await ndefTag.getCachedNdefMessage();
if (cachedMessage) {
console.info('[NFC] 使用缓存消息');
}
// 3. 标签连接保持
await nfc.setTagConnectionTimeout(5000); // 5秒超时
// 4. 批量写入
await ndefTag.writeNdefMessageBatch([
message1,
message2
]);
5.3 性能优化
// 1. 快速读取模式
await nfc.setFastReadMode(true);
// 2. 预解析NDEF
const preParsed = await nfc.preParseNdefMessage(tag);
// 3. 连接复用
const connection = await nfc.establishTagConnection(tag);
// 多次操作...
await connection.close();
六、总结
NFC标签读写是物联网应用的重要入口,掌握NDEF消息处理是NFC开发的核心技能。本文全面讲解了鸿蒙系统中NFC开发的核心要点:
核心要点回顾:
- NDEF格式:理解NDEF消息和记录的结构
- 记录类型:掌握文本、URI、MIME等常见记录的创建和解析
- 标签技术:根据标签类型选择合适的读写方式
- 容量管理:写入前检查消息大小是否适合标签
- 编码处理:正确处理文本编码和URI缩写
最佳实践建议:
- 封装NDEF工具类,简化记录创建和解析
- 实现完善的错误处理和用户提示
- 做好标签类型兼容性处理
- 使用前台调度确保及时检测
下一步学习:
- NFC卡模拟(下一篇文章)
- NFC P2P通信
- 多标签同时处理
- NFC安全机制
NFC技术简单易用,但细节不少。希望本文能帮助你掌握NFC标签读写的核心技能,让你的应用具备"碰一碰"的能力!
【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱:
cloudbbs@huaweicloud.com
- 点赞
- 收藏
- 关注作者
评论(0)