☰
OpenHarmony上React Native读写NFC标签的实践指南
2026/9/26 4:40:28 网站建设 项目流程

1. 为什么要在OpenHarmony上用React Native读写NFC

先说结论:这个组合能让你用一套JS/TypeScript代码,同时覆盖Android、iOS以及OpenHarmony三条业务线,NFC标签读取这种硬件能力则通过桥接层交给原生侧处理。

我最早接触这个需求,是给一款工业巡检设备做配套App。设备跑的是OpenHarmony标准系统,但团队里大部分前端同学只会React技术栈,如果按传统方式全部用ArkTS重写业务,估算下来光UI层就要多写两个月。当时正好OpenHarmony开源社区在推进React Native的适配工作,就把RN引了进来。真正让我决定用它做NFC读取的原因有三点:第一,RN在OpenHarmony上的架构已经能把原生能力暴露成普通JS模块,NFC这种系统服务调用起来比想象中顺;第二,NFC标签解析本身是纯逻辑操作,TS写起来比ArkTS更顺手,而且生态里现成的解析库可以直接搬;第三,同一套业务代码后面还能复用到Android平板和iOS手机上,对多端交付是实打实的减负。

当然这个方案也有代价。OpenHarmony的RN适配版本目前还落后官方主版本一大截,很多新特性不能用,遇到问题往上排查时可以参考的资料也少。所以这篇文章我不会只讲“怎么做”,还会把适配过程中踩过的坑和判断依据一起写出来。无论你是做工业巡检、智慧零售、设备配网还是门禁权限管理,只要涉及在OpenHarmony设备上读取RFID/NFC标签,这套框架都能给你一个可落地的起点。

1.1 这个组合能解决什么实际问题

把RN和NFC放一起,最典型的场景是这么几类:

  • 巡检/点检:设备上贴NFC标签,App靠近即可读取设备编号、上次维保时间,判断是否到期。
  • 库存/资产管理:仓库货架贴标签,手机或手持终端一扫,弹出物料信息,不用人工录入。
  • 配网/初始化:路由器或智能家电贴标签,App读取标签里的Wi-Fi SSID和密码字段,一键完成配置。
  • 展陈/零售互动:商品标签里存URL或智能海报,扫码枪和NFC手机都能触发跳转。

这些场景里,NFC读取只是一个“入口动作”,真正的业务跑在UI、网络、数据库这些层。用RN做上层的好处是,团队里写业务页面的同学完全不用关心硬件细节,只需要调一个readTag()方法拿到对象,然后按类型渲染即可。底层是OpenHarmony还是Android,对业务代码来说几乎透明。

1.2 方案选型背后的三个关键考量

很多人第一反应会问:OpenHarmony自己明明有NFC接口,为什么非要套一层RN?我的理由有三个。

第一是团队成本。会ArkTS的开发者数量远少于会React的,招人、培养都贵,而且业务页面一旦复杂起来,ArkTS的开发效率并不比RN高。第二是跨端复用。同一个巡检App,南方工厂用OpenHarmony手持机,北方仓库可能用Android旧平板,差异只存在于启动时的平台判断,业务代码完全复用。第三是生态。RN的NFC解析库、表单组件、图表组件极其丰富,这些在OpenHarmony原生生态里还很薄弱,与其自己造轮子,不如直接站到RN生态的肩膀上。

但我也要提醒一句:如果你们的产品只跑OpenHarmony一个平台,而且团队里没有React经验,那我建议直接用ArkTS写。引入RN会带来JS引擎开销、包体积增加、版本适配滞后等问题,单一平台场景下是纯粹的负资产。选RN的前提是“多端一致”的需求真实存在,而不是为了技术炫技。

2. NFC标签技术基础:读数据之前先弄懂这五件事

NFC和RFID经常被人混着说,其实它们是包含关系。RFID是射频识别技术的统称,工作频率覆盖低频、高频、超高频;NFC是RFID在高频(13.56MHz)段的一个子集,特点是通信距离近(通常小于10cm)、支持点对点通信和卡模拟。在OpenHarmony和Android上做标签读取,我们打交道的主要是符合ISO 14443/15693标准的Type A/B/V标签,以及NFC Forum定义的Type 1到Type 5标签。

2.1 标签类型与协议家族

要把NFC数据读对,第一步是搞清楚你面前的是哪种标签。我在项目里整理过一个速查表:

标签类型底层协议典型芯片常见用途NDEF支持
Type 1ISO 14443ANXP Topaz票务、名片只读为主
Type 2ISO 14443ANXP NTAG213/215/216智能海报、防伪支持
Type 3ISO 18092 / JIS X 6319-4Sony FeliCa交通卡、电子钱包支持
Type 4ISO 14443A/BNXP DESFire、Mifare Plus门禁、支付支持
Type 5ISO 15693NXP ICODE SLIX图书管理、药品追溯部分支持

OpenHarmony的NFC接口会通过getTagInfo()返回标签的协议类型,一般在nfcA、nfcB、nfcF、nfcV、isoDep、ndef等字段里做判断。实际业务里,我最常见的是Type 2的NTAG系列,因为便宜、稳定、写入流程简单,库存和巡检场景用得最多。如果你要做的功能是往标签里写数据,优先选Type 2或Type 4;如果只是读,Type 1也够,但兼容性要现场实测。

2.2 NDEF数据格式解析

绝大多数NFC标签存取的数据是NDEF(NFC Data Exchange Format)消息。NDEF本质上是一个TLV(Type-Length-Value)结构:一条NDEF消息由一个或多个NDEF记录组成,每个记录由记录头、类型、载荷长度、可选ID和载荷构成。

一个NDEF记录的头部第一个字节拆开看是这样的:

  • Bit 7(MB):消息起始记录,1表示这是第一条记录。
  • Bit 6(ME):消息结束记录,1表示这是最后一条记录。
  • Bit 5(CF):链式记录标志,载荷太长被拆分时置1。
  • Bit 4(SR):短记录标志,1表示载荷长度只有1字节,0表示用4字节表示长度。
  • Bit 3(IL):ID长度字段是否存在。
  • Bit 2-0(TNF):Type Name Format,即类型名格式。

这个字节我一开始也是死记硬背,后来写了个简单的解析函数才知道,SR那位特别容易忽略。很多标签芯片在写入时,如果载荷小于256字节,会用短记录格式,你按4字节去读就把长度算错了。

MB和ME同样重要。一个标签里可能有连续多个NDEF记录,比如智能海报就是“标题文本记录+URI记录+Action记录”拼在一起。解析时必须根据MB和ME判断边界,否则会把两条记录彻底搅在一起。

2.3 命名格式TNF与常用RTD类型

TNF字段决定了类型字段怎么解释。NFDEF记录头里TNF的取值范围和含义如下:

  • 0x00:空记录,类型和载荷都无效。
  • 0x01:NFC Forum well-known type,类型字段是RTD(Record Type Definition)。
  • 0x02:MIME媒体类型,比如text/plain、application/json。
  • 0x03:绝对URI,类型字段就是完整URI。
  • 0x04:外部类型,通常以urn:nfc:ext:domain:type形式存在。
  • 0x05:未知类型,载荷无类型定义。
  • 0x06:未更改类型,用于链式记录的场景。
  • 0x07:保留。

实际项目里,TNF=0x01的well-known type最常用,其中RTD字段又分三种:

  • T:文本记录,载荷前1字节是语言码长度和编码标志(bit 7为0表示UTF-8,1表示UTF-16),后面跟着语言码(如en、zh)和实际文本。
  • U:URI记录,载荷前1字节是URI标识符前缀索引,0x04代表https://,0x03代表http://,后面才是真正URI。
  • Sp:智能海报,载荷里嵌套了一个完整的NDEF消息,需要递归解析。

我在解析“U”记录时吃过一次亏。URI前缀代码表里0x00是“没有前缀”,后面直接跟完整URI;但如果标签写入工具帮你写了完整https://,而前缀索引又填了0x04,就会拼出https://https://。这种情况通常不是解析代码的问题,而是标签写入工具各自的习惯不同。稳妥的做法是解析完拼接后,用正则校验一下,把重复协议头的情况拦掉。

2.4 标签能力集差异

不是所有标签都能写,也不是所有标签都能读NDEF。比如Mifare Classic(常用于门禁)走的是私有协议,很多手机和OpenHarmony设备并不把它当作标准NDEF标签暴露出来;DESFire要选择应用ID和文件ID才能读到NDEF,而且需要先认证,涉及密钥交换的部分还是留在原生层处理比较合适。这些标签的读取权限、写入权限、密码保护策略都各不一样,做方案设计时一定要提前问清楚现场用的是什么芯片,否则代码写完了到现场发现标签类型不匹配,整个功能直接废掉。

3. 从零搭建RN+OpenHarmony开发环境

环境搭建是整套流程里最容易让人放弃的一步,因为网上资料少、版本杂。我把自己验证过的一整套流程写出来,照着做能省不少时间。先说明一下,我用的组合是:OpenHarmony 4.0 Release标准系统 + DevEco Studio 4.0 + react-native-ohos 0.72.x分支。这个组合不是最新的,但胜在稳定,社区反馈和样例代码最全。

3.1 开发板与系统版本选择

OpenHarmony不是所有设备都支持NFC,这一点特别容易被忽略。我最初用的DAYU200开发板,系统默认不带NFC驱动,翻了半天资料才发现要自己在源码里加NFC芯片驱动并重新编译系统。如果你不想折腾系统编译,直接买市面上的OpenHarmony商用设备,比如带NFC的扫码手持终端、RK3566/RK3588方案的面板,通常出厂系统已经带好NFC功能。

系统版本建议选标准系统(Standard),API Level大于等于9。OpenHarmony的NFC Tag接口在API 6就有了,但API 9之后才把权限模型稳定下来,API 8及以下很多接口行为不一致,排查起来很痛苦。客户端设备建议用API 9或10。

3.2 环境依赖安装

需要装的有这些:

  • Node.js 18+,建议LTS版本,RNOH的工具链对Node版本比较敏感,太新的版本偶尔会有兼容告警。
  • OpenHarmony SDK,通过DevEco Studio的SDK Manager下载,注意勾选API 9或10的SDK。
  • react-native-ohos相关CLI工具:@react-native-ohos/cli。
  • 开发板连接电脑用的串口工具或HDC工具(OpenHarmony的调试工具,类似Android的adb)。

安装完成后,确认hdc list targets能看到设备,就说明连接正常。NDK方面,OpenHarmony的NDK路径通常和DevEco Studio装在一起,CMake和交叉编译工具链也需要提前备好,编译原生模块时要用。

3.3 工程初始化与目录结构

用CLI初始化工程:

npx @react-native-ohos/cli init RNNfcDemo cd RNNfcDemo

初始化完成后,工程结构和标准RN项目很接近,多出的关键是harmony目录,里面放的是OpenHarmony的entry模块和原生代码。RNOH开发的原理是:把RN的C++核心、JS引擎、渲染器等编译成OpenHarmony的动态库,然后在ArkTS侧创建一个容器组件来承载RN页面。NFC这样的原生能力,通过自定义原生模块的方式暴露给JS层调用。

这个架构决定了目录里有两个需要注意的入口:

  • harmony/entry/src/main/ets/: 这里面是ArkTS写的入口和容器组件,RN页面会挂在某个组件上。
  • harmony/entry/src/main/cpp/: 这里放C++层代码,NAPI(Native API)的注册和桥接工作在这里完成。

3.4 跑通Hello World的检验清单

初始化后先别急着写NFC功能,把基础工程跑通再说。我在首次运行时遇到的问题是启动白屏,这是RNOH社区里的高频问题,后面会在排查章节详细写。这里先列一个我自己用的检验清单:

  • [ ]hdc shell param get const.product.name能返回设备型号
  • [ ] 工程能成功编译出entry-default-signed.hap
  • [ ] 通过DevEco Studio安装HAP,点击图标能进入RN页面
  • [ ] 打开DevEco的日志面板,能看到RNOH加载完成的日志,比如RNOH: ReactApplicationContext created之类
  • [ ] 在RN页面里随便写个<Text>,带中文和Emoji,确认渲染正常

这五步都过,说明RNOH基础链路是通的,后面加原生模块只是在这个框架上做增量。

4. 原生侧NFC模块实现

NFC读取这件事必须放在原生侧做,原因很简单:RN层拿不到OpenHarmony的NFC系统服务。所以我们先写一个ArkTS模块,负责扫描标签、解析NDEF数据,再用NAPI把它暴露成JS层可调用的方法。

4.1 权限声明与Tag发现回调

在module.json5里声明NFC相关权限:

{ "module": { "requestPermissions": [ { "name": "ohos.permission.NFC_TAG_READ" } ] } }

如果后面要做写入功能,还要加ohos.permission.NFC_TAG_WRITE。注意权限名字的大小写,写错了编译不报错,但运行时拿不到标签。

OpenHarmony的NFC标签发现是回调机制。在ArkTS侧,我们通过@ohos.nfc.tag模块注册监听:

import nfcTag from '@ohos.nfc.tag'; import { BusinessError } from '@ohos.base'; // 获取tagInfo需要先启动NFC扫描 function startNfcScan(): void { nfcTag.on('notify', (tagInfo: nfcTag.TagInfo) => { // 标签靠近时触发 const tagId = tagInfo.tagId; // 判断是否支持NDEF if (tagInfo.ndefSupported) { // 读取NDEF数据 } }); }

这里的tagInfo对象里有一个非常重要的字段tagInfo.ndefSupported,它在底层是通过轮询标签的ATQA、SAK和协议能力综合判断的。有些标签虽然物理上支持NDEF,但状态损坏或写入了非法数据,这个字段可能为false,这时候别硬读,让用户换一张标签更合理。

4.2 NDEF消息解析的ArkTS实现

拿到NDEF标签后,最核心的工作是解析NDEF消息。这里补一段ArkTS侧的解析代码:

import nfcTag from '@ohos.nfc.tag'; import ndef from '@ohos.nfc.tag.ndef'; import { util } from '@kit.ArkTS'; class NdefRecord { tnf: number = 0; type: Uint8Array = new Uint8Array(); id: Uint8Array = new Uint8Array(); payload: Uint8Array = new Uint8Array(); } function parseNdefMessage(tag: nfcTag.TagInfo): NdefRecord[] { const ndefTag = ndef.getNdefTag(tag); if (ndefTag == null) { return []; } const msg = ndefTag.getNdefMessage(); if (msg == null) { return []; } const records: NdefRecord[] = []; msg.forEach((record) => { const r: NdefRecord = { tnf: record.tnf, type: record.type, id: record.id, payload: record.payload }; records.push(r); }); return records; }

注意这里record.type和record.payload返回的是Uint8Array,不是字符串。很多刚接触的人直接对record.payload做toString(),结果得到一长串数字和逗号。正确做法是先用UTF-8解码,再按NDEF规则解析。文本类型还要处理语言码和编码标志位,URI类型则要查前缀表。

我把NDEF记录的解析收敛成一个纯函数,这样方便在ArkTS和JS层共用一套测试用例。解析文本记录时,UTD-8和UTF-16的判断要严格按payload首字节的bit 7来,很多国产标签写入器默认写UTF-8,但也有用UTF-16的,判断错了整段文本都是乱码。

4.3 NAPI桥接层映射

原生能力要暴露给RN层,需要通过NAPI注册一个模块。RNOH的桥接方式和标准RN略有不同,它基于ArkTS的NativeModule能力封装了一套turboModule协议。我们在ArkTS侧定义方法:

@NativeModule export class NfcModule { @Method readNdefTag(): Promise<string> { // 内部实现... } }

然后在RN侧通过TurboModuleRegistry.get或NativeModules获取:

import { NativeModules } from 'react-native'; const { NfcModule } = NativeModules;

读取后的结果我用JSON字符串传递,因为只传序列化数据最容易保证跨语言边界一致。结构体直接传对象虽然NAPI也支持,但字段多了容易踩“undefined被认为是0”的坑,不如JSON字符串干脆。

原生侧每次读取完,会返回一个对象{ tagId, records, rawMessage },其中records是一个数组,里面每个元素包含tnf、type、payloadText、payloadUri这些解析后的字段。JS层拿到后基本不需要再做二进制处理,直接渲染即可。

5. JS侧业务逻辑与界面

原生侧把数据吐出来以后,真正干活的是JS层。这也是用RN做这个项目最爽的部分——业务逻辑用TypeScript写,类型清晰,测试方便。

5.1 封装NFC读取工具类

我在JS层封装了一个NfcManager工具类,统一管理“开始扫描”“停止扫描”“读取标签”这三件事:

import { NativeModules, NativeEventEmitter } from 'react-native'; const { NfcModule } = NativeModules; const nfcEmitter = new NativeEventEmitter(NfcModule); export type NdefRecordType = { tnf: number; type: string; payloadText?: string; payloadUri?: string; }; export type NfcTagData = { tagId: string; records: NdefRecordType[]; }; class NfcManager { private scanning: boolean = false; startScan(): void { if (this.scanning) return; NfcModule.startScan(); this.scanning = true; } stopScan(): void { if (!this.scanning) return; NfcModule.stopScan(); this.scanning = false; } readTag(): Promise<NfcTagData> { return NfcModule.readNdefTag(); } onTagDetected(callback: (data: NfcTagData) => void) { return nfcEmitter.addListener('onTagDetected', callback); } } export default new NfcManager();

这里有个设计细节值得说:NFC扫描是一个持续动作,不应该每读一次就开一次关一次。标签靠近后系统触发回调,数据读出来以后可以继续扫描等待下一张标签。所以startScan和stopScan是成对的生命周期管理,在页面useEffect里启动,页面卸载时停止。

NativeEventEmitter用于原生向JS侧主动推送“标签已检测到”的事件,比轮询更优雅。轮询方案在低端设备上会频繁唤醒NFC模块,耗电且容易漏事件,不推荐。

5.2 按NDEF类型动态解析文本、URI、智能海报

原生侧返回的records已经做了初步解析,但JS侧还是需要按tnf和type字段做二次分发。比如tnf=1且type=U的记录,payload是一个URI;tnf=1且type=T的记录,payload是文本;tnf=1且type=Sp是智能海报,里面嵌套的NDEF消息会以JSON字符串形式放在payload里,JS侧要再解析一次。

我把整个解析流程写得像一个管道:

function parseNdefRecords(records: NdefRecordType[]): ParsedContent[] { return records.map((record) => { if (record.tnf === 1) { const type = record.type.toLowerCase(); switch (type) { case 't': return { kind: 'text', content: record.payloadText || '' }; case 'u': return { kind: 'uri', content: record.payloadUri || '' }; case 'sp': return { kind: 'smartposter', content: JSON.parse(record.payloadText || '{}') }; default: return { kind: 'unknown', content: record.payloadText || '' }; } } if (record.tnf === 2) { // MIME类型,比如text/plain return { kind: 'mime', content: record.payloadText || '' }; } return { kind: 'unknown', content: record.payloadText || '' }; }); }

这段代码在实际业务里可以根据场景扩展。比如读到一个URI,前端可以直接渲染成可点击的链接;读到一个文本,可以匹配正则判断是不是设备序列号,是的话自动跳转到对应的设备详情页。我把“解析”和“业务动作”分开,解析只负责把NDEF转成业务对象,业务动作由页面层根据对象类型自己去分发,这样更符合React的组件化思维。

5.3 防重复扫描与节流处理

NFC标签在感应区内停留时,系统可能连续上报十几次“标签已检测到”。如果每次回调都去刷新页面,列表会疯狂闪烁。我一开始没注意这个问题,现场演示时一贴上标签,页面内容跳了好几遍,非常尴尬。

解决方案是加节流。在NfcManager里维护一个lastReadAt时间戳,两次读取之间至少间隔1.5秒:

private lastReadAt: number = 0; private readonly throttleMs = 1500; onTagDetected(callback: (data: NfcTagData) => void) { return nfcEmitter.addListener('onTagDetected', (data) => { const now = Date.now(); if (now - this.lastReadAt < this.throttleMs) { return; } this.lastReadAt = now; callback(data); }); }

另外还有一个“是否同一张标签”的判断。因为在巡检场景里,用户可能扫完A标签后不小心又晃到B标签,需要区分是重复上报同一张还是新标签。我简单用tagId做去重,如果同一张标签连续上报,直接忽略;如果tagId变了,立即刷新。

节流间隔不宜设太长,否则用户快速连续扫两张不同标签时会漏掉第二张。1.2到1.5秒是现场实测比较舒服的区间,既不会防抖过头,也不会重复刷新。

5.4 业务界面示例

以巡检为例,一个最简页面是这样:

import React, { useEffect, useState } from 'react'; import { View, Text, TouchableOpacity, ScrollView } from 'react-native'; import NfcManager from './NfcManager'; export function NfcReaderScreen() { const [tagInfo, setTagInfo] = useState<NfcTagData | null>(null); const [scanning, setScanning] = useState(false); useEffect(() => { const sub = NfcManager.onTagDetected((data) => { setTagInfo(data); }); return () => { sub.remove(); NfcManager.stopScan(); }; }, []); const start = () => { NfcManager.startScan(); setScanning(true); }; return ( <View style={{ flex: 1, padding: 20 }}> <TouchableOpacity onPress={start} disabled={scanning}> <Text>{scanning ? '扫描中...' : '开始扫描'}</Text> </TouchableOpacity> {tagInfo && ( <ScrollView style={{ marginTop: 20 }}> <Text>标签ID: {tagInfo.tagId}</Text> {tagInfo.records.map((rec, idx) => ( <Text key={idx}> {rec.payloadText || rec.payloadUri || '未知内容'} </Text> ))} </ScrollView> )} </View> ); }

页面逻辑非常简单,因为复杂的事情都在原生解析和工具类里做掉了。实际项目中,我还会加一个“已扫描设备列表”,把每次读到的标签按时间戳存进列表,方便回看。这个列表可以直接用SectionList来做,按天分组。

6. 常见问题与排查实录

RNOH加NFC这套组合,网上可搜到的踩坑记录不多,很多问题只能现场试错。我把实际遇到的高频问题整理成了一份速查表,按排查顺序排列。

现象可能原因排查方法
标签贴近开发板完全无反应NFC服务未开启或驱动未加载系统设置查看NFC开关,hdc shell hidumper -s NFC查看服务状态
能扫描到标签但拿不到NDEF数据标签未格式化或为空标签用专业写入工具先写入一条合法NDEF记录再测试
读出来的文本乱码UTF-8/UTF-16编码判断错误检查payload首字节bit 7标志位
连续扫描时页面重复刷新缺少节流/去重在NfcManager里加时间戳和tagId去重
RN页面首次打开白屏RNOH加载慢或日志没输出排查JS bundle路径、等待时间、重启App
编译报错NAPI类型不匹配ArkTS和C++类型映射错误统一用string和JSON.stringify传参
权限请求成功但读取失败动态权限二次确认未处理在页面启动时主动申请并确认授权结果

6.1 扫描无响应的五步排查

如果标签贴近设备后,RN页面什么事都没发生,我的排查顺序是:

第一步,确认真机NFC开关是开的。OpenHarmony的部分开发板默认关闭NFC,而且不提示用户,这个坑最浅但最容易忽略。第二步,确认权限有加且已动态授权。OpenHarmony的权限模型要求运行时弹窗确认,如果用户点了拒绝,后面所有调用都是静默失败。第三步,用系统自带工具验证标签。找一台Android手机装个NFC Tools,把同一张标签贴上,如果Android也读不到,说明标签坏了或者格式不对。第四步,看HDC日志里有没有NFC服务报错。hdc shell hilog | grep NFC是最直接的排查手段,错误信息里通常会写明协议不匹配或超时。第五步,检查标签类型。如果标签是Felica(Type 3)而设备NFC天线只适配了Type A/B,读不到是正常的。

这个排查顺序能覆盖90%以上的“无反应”问题。别一上来就怀疑代码,硬件和系统层面的问题概率更大。

6.2 NDEF数据读出来是乱码

乱码问题的根源几乎都在编码判断上。NDEF文本记录的第一字节,bit 7为0表示UTF-8,为1表示UTF-16。很多标签写入器在写中文时默认用UTF-8,但部分进口工具默认用UTF-16,而文本长度记录的是字节数不是字符数,解析错编码会直接导致乱码或截断。

我在原生解析代码里,加了一个回退机制:如果按UTF-8解码后发现包含大量不可见字符(比如U+FFFD替换符),就尝试按UTF-16解码一次。这个策略在实测里能把兼容性拉高不少。但要注意,回退解码只能用于展示型场景,不能用于后续的逻辑匹配,因为不可靠。生产环境还是要靠写入端约定统一的编码标准。

6.3 TagLost与超时错误

TagLost是NFC开发中特别常见的异常,意思是“标签在通信过程中离开了感应区”或“设备在等待标签响应时标签已移走”。这个问题在手持设备上尤其明显,因为人手会难免抖动。遇到TagLost,正确做法不是报错,而是提示用户重新贴近。

我在原生层捕获TagLostException后,会向JS层抛一个友好错误码TAG_LOST,JS侧拿到后弹一个Toast“请重新贴近标签”,同时保持扫描状态不变。这样用户体验会自然很多,而不是直接退出页面。

6.4 真机调试与日志查看技巧

RN页面和OpenHarmony原生日志是两套体系,排查时要两边一起看。

RN侧的console.log可以通过DevEco Studio的Log面板看到,因为RNOH会把JS日志转发到hilog。但注意,console.info和console.warn的输出级别不同,DevEco默认过滤了verbose级别的日志,有时候看不到是级别问题,不是没打印。

原生侧日志统一用hilog查看:

hdc shell hilog -r hdc shell hilog | grep -i nfc hdc shell hilog | grep -i ndef

如果怀疑RNOH本身有问题,可以开RNOH的调试模式,看RNOH_LOG级别的输出。常见白屏问题里,最多的是JS bundle路径配置错误,或者双端(OpenHarmony和RN)的版本不匹配,日志里通常会有明确的Unable to load script或Dependency mismatch字样。

我还建议在原生解析代码里加上分阶段日志:收到onNotify打一条、检查到NDEF能力打一条、解析完成打一条。这样一旦出现问题,通过日志能快速定位是“没检测到”“不支持NDEF”还是“解析异常”。这种埋点习惯在嵌入式平台上特别管用,因为现场排查往往没有断点调试条件。

写在最后的一点个人体会

NFC读取这种功能,说难不难,无非是“拿到tag、解析NDEF、渲染数据”三步。但真要落到OpenHarmony加RN这套组合上,细节却比想象中多得多。我个人最大的体会是,跨语言边界传数据时一定要收敛数据结构,能传字符串绝不传对象,能JSON序列化绝不裸传。这个原则帮我躲过了很多NAPI类型映射的暗坑。

另外一点,如果你打算在项目里批量写入标签(比如给一批库存商品快速写入初始信息),我强烈建议把写入能力也做成原生模块,和读取放到同一个模块里统一管理。写入逻辑比读取复杂,需要校验标签状态、按区块写入、验证写入结果,用RN写纯逻辑还可以,但涉及底层扇区操作最好还是留在ArkTS侧。一个库里把读写都封好,后续维护起来会轻松很多。

最后再分享一个小技巧:开发期间准备三张不同厂商的标签放在桌上,一张Type 2、一张Type 4、一张只支持RFID不支持NDEF的。每次改完代码,三张全扫一遍,能让你在30秒内发现兼容性回归。这个习惯我在做NFC项目时一直保留,建议你也试试。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询