NDEF详解之NFC唤醒微信小程序

该文章已生成可运行项目,

前言:

【NFC标签打开小程序】这个功能首先基于URL Scheme,要实现这个功能大致流程是:

  • 获取URL Scheme
  • 再将URL Scheme以NDEF的格式写入进NFC标签中。

写入的数据格式微信官方文档上说明如图1-1:

图1-1

看到这,如果你一脸懵,完全不知道啥意思,就应该往下了解下NDEF数据格式,做一下基础的知识储备了。

一、了解NDEF

1、 NDEF是什么?

NDEF全称:nfc data exchange format 即 nfc 数据交换格式;ndef的message由多个record组成;而record又有record头部(header)和负载(payload)构成,这里会详解record的构成,这对我们后面需要实现的功能非常重要!

2、 record的结构

record是由header和payload构成,如下图:

图1-2

如图1-2所示,第一个字节包含MB,ME,CF,SR,IL,TNF,它们的功能作用如下表1-1:

MB

message begin:消息开始的地方 一般用于开头 且置1

ME

message end:消息结束的地方 一般用于结尾 且置1

CF

chunk flag)是否分块

SR

(short record)如果设置该值,则上图的 pay length 只需要1个, 表示payload数据长度被限制在255个字节之内。

IL

(id_length标志)表示 header 是否含有 id 和 id length 这两个字段

TNF

(type name format )用于指明 payload 的类型 具体可见图

表1-1

3、了解TNF

TNF的作用是用于指明payload的类型,如果payload中tnf写错,那就会导致在数据传输中对payload的消息解析失败。所以了解下tnf中每个取值代表的含义很重要,具体见下表1-2:

tnf取值

tnf名

作用

0x00

Empty

表示是一个空的 nfc record

0x01

Nfc forum well-Known type 

众所周知类型,其中定义了常见数据格式

0x02

MIME

多用途 internet 扩展

0x03

Absolute URI

绝对的 uri 地址

0x04

nfc forum external type 

为第三方组织定义的类型

0x05

unknown

代表Payload中的数据类型未知

0x06

unchanged

这种类型的数据用于NFC Record分片

表1-2

其中nfc forum well-Known type 定义了一些常见的数据格式 如下:

  • URI Record Type:用于存储URI数据,对应Type字段取值为"U"。
  • Text Record Type:用于存储文本数据,对应Type字段取值为"T"。
  • Signature Record Type:用于存储数字签名数据,对应Type字段取值为"Sig"。
  • Smart Poster Record Type:智能海报,用于存储与该海报相关的一些资讯信息,如图片、相关介绍等,对应Type字段取值为"Sp"
  • Generic Control Record Type:用于传递控制信息,对应Type字段取值为"Gc"

了解到这里,再回去看图1-1,就能大致看懂了,第一个record的需要携带url schame,所以payload的类型tnf为0x01,type取值‘U’,表示存储的URI数据;安卓还需要一个AAR来拉取微信,则需要再传一个类型为0x04的payload。所以在代码中我们写入record的数据如下(nfc存储的数据是以字节存在的,所以在传进去时需要进行转换,这里用的是封装好的string2ArrayBuffer方法):

按照上面的数据格式,成功写入nfc后,将标签分别于安卓和苹果手机贴靠,发现虽然都能感应标签,却不能打开对应的微信小程序,如下面图:

图3-1 苹果感应结果图

图3-2 安卓感应结果图

这是为什么呢?看来还得了解下payload的结构。

4、payload结构

这里主要了解下常用的payload类型的结构:

  • Text record payload(文本型)
  • Url record payload(网址型)

(1)text record payload:

文本型payload组成结构有三部分:status,语言码,实际需要写入的Text内容;具体说明如下表:

D1(1101 0001B)对应 MB ME CF SR IL TNF TNF 为 0x01

01 type 的长度

0F payload 的长度

54 type 的类型 对应 ”T“

02 对应语言码字段的长度

65 6E 表示为 “en”

68 65 6C 6F 2C 77 6F 72 6C 64 21 payload 的内容 对应 ASCII 码 结果为 ”Hello,World!“。

ascII 对照表:

ASCII码表,ASCII码一览表,ASCII码对照表完整版-ASCII码中文站

(2)Uri record payload:

网址型payload组成结构有两部分:ID code,实际需要写入的uri内容;其中ID code占总内容的一个字节,剩下的字节就是实际写入的uri的字节数,详细说明如下:

其中ID code的值代表了不同的协议头:

了解到这里,就能知道我们在nfc中写入的url schame 为什么不能被解析成功而实现跳转,原因在于我们在传入payload的时候直接传入了实际的uri内容,而少了ID code部分,所以解析出的内容手机无法识别它是什么协议,从而无法实现正确跳转。正确的做法是,首先需要了解到我们传入uri是否有带协议头,就如我们这里传入的的url schame的内容大致为weixin://xxxxx,它是有带协议的,所以我们再写入payload的内容时还需要在头部加入【无前缀】的ID Code,也就是在uri内容转成字节buffer后再在字节组前面添加0,最后结果就如预料中一样,能正常实现安卓和苹果唤醒小程序的功能。

图4-1 正确写入uri型payload数据

图4-2 遗漏ID code写入的payload数据

  • 在arrayBuffer前强制加[00] 的方法如下:

export function payloadFormate(openlink) {
  const arrayBuffer = string2ArrayBuffer(openlink);
  const zeroArrayBuffer = new Uint8Array([0]).buffer;
  const resultArrayBuffer = new Uint8Array(
    zeroArrayBuffer.byteLength + arrayBuffer.byteLength
  );
  resultArrayBuffer.set(new Uint8Array(zeroArrayBuffer), 0);
  resultArrayBuffer.set(new Uint8Array(arrayBuffer), 1);
  const payload = resultArrayBuffer.buffer;
  return payload;
}

本文章已经生成可运行项目
<think>我们正在处理一个关于微信小程序中读取NFC标签NDEF数据时偶尔失败的问题。根据用户提供的代码片段和引用,我们可以分析可能的原因和解决方案。 用户代码中,主要做了以下操作: 1. 初始化NFC适配器(`wx.getNFCAdapter()`) 2. 开始发现NFC标签(`startDiscovery`),指定了NDEF格式(`techTypes: ['android.nfc.tech.Ndef']`) 3. 设置监听器(`setupListeners`)来处理发现的NFC标签(`onDiscovered`) 然而,用户遇到偶尔读取失败的情况。 可能的原因分析: 1. **NFC标签的类型支持问题**:虽然指定了NDEF格式,但并非所有NFC标签都支持NDEF格式,或者标签可能未格式化或格式不正确。 2. **读取时机问题**:当标签进入射频场时,小程序可能还未完全准备好监听,或者标签离开太快。 3. **多标签干扰**:同时存在多个标签时,可能会导致读取失败。 4. **微信小程序API的稳定性**:微信小程序NFC API可能存在一些稳定性问题,尤其是在不同的设备和微信版本上。 5. **标签数据量过大**:如果标签中的数据量较大,读取可能需要更长时间,而小程序可能因超时而失败。 6. **设备兼容性问题**:不同的手机厂商对NFC的支持程度不同。 解决方案或优化建议: 1. **确保标签支持NDEF**:检查标签是否支持NDEF格式,并且已经正确写入NDEF数据。可以使用其他NFC读取工具(如手机自带的NFC工具)验证。 2. **增加错误处理**:在`startDiscovery`和`onDiscovered`中,确保捕获并处理所有可能的错误。 3. **重试机制**:在读取失败时,可以尝试重新启动发现过程(注意不要频繁重启,避免性能问题)。 4. **检查标签是否离开**:在`onDiscovered`回调中,如果读取失败,可以尝试重新读取,但要注意避免死循环。 5. **使用超时机制**:设置一个合理的超时时间,如果在规定时间内没有读取到,则重启发现过程。 6. **监听标签离开事件**:微信小程序NFC API提供了`onTagLost`事件,可以在标签离开后重新启动发现,以准备下一次读取。 7. **检查微信版本和基础库**:确保微信版本和基础库版本支持NFC功能,并且是最新的。 修改代码示例(基于用户提供的代码): 我们可以增加重试机制和错误处理。注意:重试次数应有限制,避免无限循环。 具体修改如下: 1. 增加重试计数器(`retryCount`)和最大重试次数(`maxRetry`)变量。 2. 在失败时(如`startDiscovery`失败或读取失败),进行重试。 3. 在`onDiscovered`回调中,如果读取成功,则重置重试计数器;如果读取失败,则进行重试。 4. 注意:在重试之前,可能需要先停止当前的发现过程(`stopDiscovery`),然后再重新启动。 但是,请注意,微信小程序NFC API中,`stopDiscovery`是一个异步操作,我们需要确保停止成功后再重新启动。 修改后的代码结构: ```javascript // ... 原有变量 const maxRetry = 3; // 最大重试次数 let retryCount = 0; // 当前重试次数 // 初始化NFC const initNfc = () => { // ... 原有代码 } // 开始发现NFC标签 const startDiscovery = () => { if (!nfcAdapter.value || isReading.value) return; nfcAdapter.value.startDiscovery({ techTypes: ['android.nfc.tech.Ndef'], success: () => { showToast('nfc监听启动成功'); isReading.value = true; setupListeners(); retryCount = 0; // 重置重试次数 }, fail: (err) => { errorMessage.value = `NFC启动失败: ${err.errMsg}`; console.error('NFC错误:', err); // 如果当前重试次数小于最大重试次数,则重试 if (retryCount < maxRetry) { retryCount++; console.log(`尝试重新启动NFC,第${retryCount}次`); setTimeout(() => { startDiscovery(); }, 1000); // 延迟1秒后重试 } else { // 超过最大重试次数,提示用户 showToast('NFC启动失败,请重试'); } } }); } // 处理读取到的nfc数据 const setupListeners = () => { nfcAdapter.value.onDiscovered((res) => { console.log('读取到的nfc数据', res); // 如果读取成功,重置重试计数器(如果有在读取失败时重试的逻辑) retryCount = 0; // 处理数据... }); // 增加错误监听 nfcAdapter.value.onError((err) => { console.error('NFC发生错误:', err); // 发生错误时,尝试重启发现 if (isReading.value && retryCount < maxRetry) { retryCount++; console.log(`读取过程中出错,尝试重新启动,第${retryCount}次`); // 先停止发现,然后重启 nfcAdapter.value.stopDiscovery({ success: () => { startDiscovery(); }, fail: (stopErr) => { console.error('停止发现失败:', stopErr); // 即使停止失败,也尝试重启 startDiscovery(); } }); } }); }; // 另外,可以监听标签离开事件,以便在标签离开后重新准备读取 // 在setupListeners中增加 nfcAdapter.value.onTagLost(() => { console.log('标签离开'); // 标签离开后,可以做一些清理工作,或者准备下一次读取 }); ``` 注意:以上代码仅为示例,实际应用中需要根据具体情况进行调整。 另外,用户提到是偶尔失败,所以可能不是每次都会发生。因此,除了重试机制外,还应该注意日志记录,以便在失败时能够获取详细的错误信息。 建议用户记录每次失败时的错误信息(包括errMsg和错误时间等),这样有助于进一步分析。 同时,参考引用[2]中提到的,如果是M1卡(MifareClassic1K)等加密卡,可能需要使用不同的技术类型(techTypes)和方法。但用户指定的是NDEF格式,所以应该只处理NDEF标签。如果用户同时需要处理非NDEF标签(如M1卡),则需要调整techTypes,并处理不同的卡类型。但根据问题描述,用户是读取NDEF数据,所以这里我们假设标签是NDEF格式。 最后,提醒用户注意手机NFC功能的开启以及微信权限的获取(微信小程序需要用户授权NFC权限)。 如果以上方法仍不能解决问题,可能需要检查手机系统对NFC的支持情况,或者尝试在不同的手机上测试。 希望这些建议能帮助用户解决问题。</think>针对微信小程序中读取NFC标签NDEF数据时出现的偶尔失败问题,结合技术文档和实际开发经验,以下是可能的原因和解决方案: ### 可能原因分析 1. **标签距离/位置不稳定** NFC读取距离短(通常<10cm),标签轻微偏移或角度变化会导致信号中断。根据引用[1],`onDiscovered`事件可能因物理干扰而无法持续触发。 2. **NDEF格式兼容性问题** 某些标签未完全遵循NDEF标准格式(如缺少NDEF头标识),微信SDK解析时会失败(引用[2]提到M1卡需特殊处理)。 3. **多标签干扰** 当多个NFC标签同时进入射频场时,微信API可能无法正确聚焦目标标签。 4. **微信底层API限制** 微信小程序`getNFCAdapter()`存在已知问题: ```javascript // 引用[1]中初始化代码 if (!wx.getNFCAdapter) { errorMessage.value = '当前微信版本不支持NFC' } ``` 部分安卓机型需微信8.0.16+版本才支持完整NFC功能。 5. **未处理异步冲突** 频繁调用`startDiscovery()`/`stopDiscovery()`可能导致状态冲突(如`isReading`未及时更新)。 --- ### 解决方案 #### 1. 增强物理稳定性(关键) ```javascript // 监听标签离开事件(引用[2]扩展) nfcAdapter.value.onTagLost(() => { console.log("标签离开,准备重试"); setTimeout(() => startDiscovery(), 300); // 延迟重启发现 }); ``` #### 2. 添加NDEF格式校验 ```javascript // 在onDiscovered回调中增加格式检查 nfcAdapter.value.onDiscovered((res) => { if (!res.techTypes.includes('android.nfc.tech.Ndef')) { console.error("非NDEF格式标签"); return; } // 解析NDEF数据 const ndefRecords = res.ndefMessage?.records || []; if (ndefRecords.length === 0) { console.error("NDEF数据为空"); } }); ``` #### 3. 实现自动重试机制 ```javascript let retryCount = 0; const MAX_RETRY = 2; const setupListeners = () => { nfcAdapter.value.onDiscovered((res) => { retryCount = 0; // 成功后重置计数器 console.log('成功读取NDEF', res.ndefMessage); }); nfcAdapter.value.onError((err) => { if (retryCount < MAX_RETRY) { retryCount++; nfcAdapter.value.stopDiscovery(() => { setTimeout(() => startDiscovery(), 500); }); } }); }; ``` #### 4. 兼容性处理(必须) ```javascript // 检查微信基础库版本 wx.getSystemInfo({ success: (res) => { const SDKVersion = res.SDKVersion; if (compareVersion(SDKVersion, '2.16.0') < 0) { wx.showModal({ title: "请升级微信到最新版本" }); } } }); // 版本比较工具 function compareVersion(v1, v2) { const arr1 = v1.split('.').map(Number); const arr2 = v2.split('.').map(Number); for (let i = 0; i < Math.max(arr1.length, arr2.length); i++) { const num1 = arr1[i] || 0, num2 = arr2[i] || 0; if (num1 !== num2) return num1 - num2; } return 0; } ``` #### 5. 优化发现流程 ```javascript // 确保串行操作 const safeRestartDiscovery = () => { if (isReading.value) { nfcAdapter.value.stopDiscovery({ success: () => startDiscovery(), fail: (err) => console.error("停止失败", err) }); } else { startDiscovery(); } }; ``` --- ### 关键注意事项 1. **设备要求**: - 安卓需支持NFC-A协议(ISO14443 Type A) - iPhone仅支持后台NDEF读取(需iOS13+) 2. **标签规范**: - 使用NTAG213/215/216等NDEF兼容标签 - 避免使用Mifare Classic等加密卡(引用[2]) 3. **权限配置**: ```json // app.json "permission": { "scope.userLocation": {}, "nfc": {} } ``` > **实测建议**:在`onDiscovered`失败时主动调用`wx.getNFCAdapter().getTag()`进行原始数据备份,协助诊断格式问题[^1][^2]。 --- ### 相关问题 1. 如何检测微信小程序是否支持当前手机的NFC功能? 2. NDEF标签和MifareClassic标签在微信小程序中的处理方式有何不同? 3. 当NFC读取失败时,如何在小程序中实现自动重连机制? [^1]: uniapp 微信小程序读取nfc标签数据 [^2]: 微信小程序NFC卡读写,MifareClassic1K卡,M1卡片读写教程
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值