• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# NFC标签读写开发指南
2
3<!--Kit: Connectivity Kit-->
4<!--Subsystem: Communication-->
5<!--Owner: @amunra03-->
6<!--Designer: @wenxiaolin-->
7<!--Tester: @zs_111-->
8<!--Adviser: @zhang_yixin13-->
9
10## 简介
11近场通信(Near Field Communication,NFC)是一种短距高频的无线电技术,在13.56MHz频率运行,通信距离一般在10厘米距离内。电子设备可以通过NFC通信技术和NFC标签通信,从标签中读取数据,或写入数据到标签。<br>
12NFC标签支持一种或多种通信技术,具体技术如下:
13- NfcA (也称为 ISO 14443-3A)
14- NfcB (也称为 ISO 14443-3B)
15- NfcF (也称为 JIS 6319-4)
16- NfcV (也称为 ISO 15693)
17- IsoDep (也称为 ISO 14443-4)
18- NDEF
19- MifareClassic
20- MifareUltralight
21
22## 场景介绍
23电子设备通过NFC天线位置触碰NFC标签卡片,完成NFC标签卡片的读取或写入。从使用场景上,可以分成NFC标签前台读写和NFC标签后台读写。
24- NFC标签前台读写<br>
25前台读写是指在触碰NFC标签之前,用户先在电子设备上打开特定的应用程序,用户明确想使用所打开的应用程序和NFC标签进行读写操作。用户打开应用程序在前台,并且进入应用的刷卡页面之后,电子设备触碰NFC标签,只会把读取到的卡片分发给前台应用。
26- NFC标签后台读写<br>
27后台读写是指不打开特定的NFC标签应用程序,电子设备触碰发现NFC标签后,根据NFC标签的技术类型,分发给能够处理的应用程序。如果能匹配到多个应用程序,则弹出应用选择器列举出应用列表给用户手动选择。用户选择指定的应用后,自动跳转到应用程序的NFC标签读写卡页面。
28- 标签读写约束条件<br>
29不管是前台读写,还是后台读写,电子设备能够发现NFC标签的前提条件是设备必须是亮屏和解锁状态。
30
31## 接口说明
32
33NFC标签读写完整的JS API说明以及实例代码请参考:[NFC标签接口](../../reference/apis-connectivity-kit/js-apis-nfcTag.md)。
34
35获取不同技术类型标签对象的接口说明如下表,根据不同技术的标签对象来执行NFC标签的读写。
36
37| 接口名                             | 功能描述                                                                       |
38| ---------------------------------- | ------------------------------------------------------------------------------ |
39| getNfcA(tagInfo: TagInfo): NfcATag                    | 获取NfcA技术类型的标签对象。                                                               |
40| getNfcB(tagInfo: TagInfo): NfcBTag                      | 获取NfcB技术类型的标签对象。                                                                |
41| getNfcF(tagInfo: TagInfo): NfcFTag                 | 获取NfcF技术类型的标签对象。                                                               |
42| getNfcV(tagInfo: TagInfo): NfcVTag                  | 获取NfcV技术类型的标签对象。                                                                |
43| getIsoDep(tagInfo: TagInfo): IsoDepTag | 获取IsoDep技术类型的标签对象。                                                                |
44| getNdef(tagInfo: TagInfo): NdefTag | 获取NDEF技术类型的标签对象。                                                            |
45| getMifareClassic(tagInfo: TagInfo): MifareClassicTag         | 获取MifareClassic技术类型的标签对象。                                                        |
46| getMifareUltralight(tagInfo: TagInfo): MifareUltralightTag         | 获取MifareUltralight技术类型的标签对象。                                                     |
47
48## 开发准备
49
50### NFC标签前台读写或后台读写的选择
51NFC标签读写应用开发者根据业务需要,可以选择实现前台读卡或者后台读卡。两种不同的读卡方式,代码实现上会存在一些差异。
52- NFC标签前台读写<br>
531. 在配置文件module.json5中,不需要静态声明过滤读取NFC标签的技术类型,而是通过[tag.registerForegroundDispatch](../../reference/apis-connectivity-kit/js-apis-nfcTag.md#tagregisterforegrounddispatch10)或者[tag.on](../../reference/apis-connectivity-kit/js-apis-nfcTag.md#tagon11)来完成动态注册。
542. 通过registerForegroundDispatch或on来动态注册前台读写标签时,入参中必须指定需要读取NFC标签的技术类型。
553. 如果选择registerForegroundDispatch注册,当应用运行在前台并进入该页面,NFC的卡模拟功能在打开时,可以同时完成刷卡。如果选择tag.on注册,当应用运行在前台并进入该页面时,NFC的卡模拟是关闭的,无法同时进行刷卡功能。
564. 当应用页面切换到后台时,需要显式调用[tag.unregisterForegroundDispatch](../../reference/apis-connectivity-kit/js-apis-nfcTag.md#tagunregisterforegrounddispatch10)或者[tag.off](../../reference/apis-connectivity-kit/js-apis-nfcTag.md#tagoff11)来取消注册,退出前台读卡优先功能。
57- NFC标签后台读写<br>
581. 在配置文件module.json5中,需要静态声明过滤读取NFC标签的技术类型。根据业务需要至少定义一种读标签的技术类型,‘tag-tech/’是前缀,后面跟着技术类型描述。
592. 技术类型的描述字符,必须完整匹配并区分大小写,需要严格匹配。
60
61> **注意:**
62> - 从API version 9之后的应用开发新增支持Stage模型,作为目前主推并长期演进的模型。
63> - NFC标签读写示例代码的提供,全部按照Stage模型来说明。
64
65## 开发步骤
66
67### 前台读取标签
681. 在module.json5文件中声明NFC标签读取的权限,以及声明NFC标签特定的action。
692. import需要的tag模块和其他相关的模块。
703. 判断设备是否支持NFC能力。
714. 调用tag模块中前台优先的接口,使能前台应用程序优先处理所发现的NFC标签功能。
725. 获取特定技术类型的NFC标签对象。
736. 执行读写接口完成标签数据的读取或写入数据到标签。
747. 退出应用程序NFC标签页面时,调用tag模块退出前台优先功能。
75
76```ts
77    "abilities": [
78      {
79        "name": "EntryAbility",
80        "srcEntry": "./ets/entryability/EntryAbility.ts",
81        "description": "$string:EntryAbility_desc",
82        "icon": "$media:icon",
83        "label": "$string:EntryAbility_label",
84        "startWindowIcon": "$media:icon",
85        "startWindowBackground": "$color:start_window_background",
86        "exported": true,
87        "skills": [
88          {
89            "entities": [
90              "entity.system.home"
91            ],
92            "actions": [
93              "ohos.want.action.home",
94
95              // actions必须包含"ohos.nfc.tag.action.TAG_FOUND"
96              "ohos.nfc.tag.action.TAG_FOUND"
97            ]
98          }
99        ]
100      }
101    ],
102    "requestPermissions": [
103      {
104        // 添加NFC标签操作的权限
105        "name": "ohos.permission.NFC_TAG",
106        "reason": "$string:app_name",
107      }
108    ]
109```
110
111```ts
112import { tag } from '@kit.ConnectivityKit';
113import { BusinessError } from '@kit.BasicServicesKit';
114import { hilog } from '@kit.PerformanceAnalysisKit';
115import { AbilityConstant, UIAbility, Want, bundleManager } from '@kit.AbilityKit';
116
117let nfcTagElementName: bundleManager.ElementName;
118let foregroundRegister: boolean;
119
120async function readerModeCb(error : BusinessError, tagInfo : tag.TagInfo) {
121  if (!error) {
122    // 获取特定技术类型的NFC标签对象
123    if (tagInfo == null) {
124      hilog.error(0x0000, 'testTag', 'readerModeCb tagInfo is invalid');
125      return;
126    }
127    if (tagInfo.uid == null) {
128      hilog.error(0x0000, 'testTag', 'readerModeCb uid is invalid');
129      return;
130    }
131    if (tagInfo.technology == null || tagInfo.technology.length == 0) {
132      hilog.error(0x0000, 'testTag', 'readerModeCb technology is invalid');
133      return;
134    }
135
136    // 标签里面可能支持多种技术类型,选择特定的技术类型接口,完成标签数据的读取或写入
137    // 下面示例代码,使用IsoDep完成标签数据的读取或写入
138    let isoDep : tag.IsoDepTag | null = null;
139    for (let i = 0; i < tagInfo.technology.length; i++) {
140      if (tagInfo.technology[i] == tag.ISO_DEP) {
141        try {
142          isoDep = tag.getIsoDep(tagInfo);
143        } catch (error) {
144          hilog.error(0x0000, 'testTag', 'readerModeCb getIsoDep error = %{public}s', JSON.stringify(error));
145          return;
146        }
147      }
148      // 也可以按需选择其它类型的技术读写标签
149    }
150    if (isoDep == null) {
151      hilog.error(0x0000, 'testTag', 'readerModeCb getIsoDep is invalid');
152      return;
153    }
154
155    // 使用IsoDep技术连接到NFC标签
156    try {
157        isoDep.connect();
158    } catch (error) {
159        hilog.error(0x0000, 'testTag', 'readerModeCb isoDep.connect() error = %{public}s', JSON.stringify(error));
160        return;
161    }
162    if (!isoDep.isConnected()) {
163      hilog.error(0x0000, 'testTag', 'readerModeCb isoDep.isConnected() false.');
164      return;
165    }
166
167    // 发送指令到已连接的标签,获取标签的响应数据
168    let cmdData = [0x01, 0x02, 0x03, 0x04]; // 修改为正确的访问标签的指令数据
169    try {
170      isoDep.transmit(cmdData).then((response : number[]) => {
171        hilog.info(0x0000, 'testTag', 'readerModeCb isoDep.transmit() response = %{public}s.', JSON.stringify(response));
172      }).catch((err : BusinessError)=> {
173        hilog.error(0x0000, 'testTag', 'readerModeCb isoDep.transmit() err = %{public}s.', JSON.stringify(err));
174        return;
175      });
176    } catch (businessError) {
177      hilog.error(0x0000, 'testTag', 'readerModeCb isoDep.transmit() businessError = %{public}s.', JSON.stringify(businessError));
178      return;
179    }
180  } else {
181    hilog.info(0x0000, 'testTag', 'readerModeCb readerModeCb error %{public}s', JSON.stringify(error));
182  }
183}
184
185export default class EntryAbility extends UIAbility {
186  onCreate(want: Want, launchParam: AbilityConstant.LaunchParam) {
187    hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onCreate');
188
189    // 判断设备是否支持NFC能力
190    if (!canIUse("SystemCapability.Communication.NFC.Core")) {
191      hilog.error(0x0000, 'testTag', 'nfc unavailable.');
192      return;
193    }
194
195    // 根据应用程序信息,初始化正确的值
196    nfcTagElementName = {
197      bundleName: want.bundleName ?? '',
198      abilityName: want.abilityName ?? '',
199      moduleName: want.moduleName,
200    }
201  }
202
203  onForeground() {
204    // 应用进入前台,调用tag模块中前台优先的接口,使能前台应用程序优先处理所发现的NFC标签功能
205    hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onForeground');
206    if (nfcTagElementName != undefined) {
207      // 根据业务需要,选择需要读取标签的通信技术
208      let techList : number[] = [tag.NFC_A, tag.NFC_B, tag.NFC_F, tag.NFC_V];
209      try {
210        tag.on('readerMode', nfcTagElementName, techList, readerModeCb);
211        foregroundRegister = true;
212      } catch (error) {
213        hilog.error(0x0000, 'testTag', 'on readerMode error = %{public}s', JSON.stringify(error));
214      }
215    }
216  }
217
218  onBackground() {
219    hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onBackground');
220    // 退出应用程序NFC标签页面时,调用tag模块退出前台优先功能
221    if (foregroundRegister) {
222      foregroundRegister = false;
223      try {
224        tag.off('readerMode', nfcTagElementName);
225      } catch (error) {
226        hilog.error(0x0000, 'testTag', 'off readerMode error = %{public}s', JSON.stringify(error));
227      }
228    }
229  }
230}
231```
232
233### 后台读取标签
2341. 在module.json5文件中声明NFC标签读取的权限,声明NFC标签特定的action,以及声明本应用程序的能够处理的NFC标签技术类型。
2352. import需要的tag模块和其他相关的模块。
2363. 获取特定技术类型的NFC标签对象。
2374. 执行读写接口完成标签数据的读取或写入数据到标签。
238
239```ts
240    "abilities": [
241      {
242        "name": "EntryAbility",
243        "srcEntry": "./ets/entryability/EntryAbility.ts",
244        "description": "$string:EntryAbility_desc",
245        "icon": "$media:icon",
246        "label": "$string:EntryAbility_label",
247        "startWindowIcon": "$media:icon",
248        "startWindowBackground": "$color:start_window_background",
249        "exported": true,
250        "skills": [
251          {
252            "entities": [
253              "entity.system.home"
254            ],
255            "actions": [
256              "ohos.want.action.home",
257
258              // actions必须包含"ohos.nfc.tag.action.TAG_FOUND"
259              "ohos.nfc.tag.action.TAG_FOUND"
260            ],
261
262            // 根据业务需要至少定义一种读标签的技术类型,‘tag-tech/’是前缀,后面跟着技术类型描述
263            "uris": [
264              {
265                  "type":"tag-tech/NfcA"
266              },
267              {
268                  "type":"tag-tech/IsoDep"
269              }
270              // 必要时可添加其他技术类型
271              // 例如: NfcB/NfcF/NfcV/Ndef/MifareClassic/MifareUL/NdefFormatable
272            ]
273          }
274        ]
275      }
276    ],
277    "requestPermissions": [
278      {
279        // 添加NFC标签操作的权限
280        "name": "ohos.permission.NFC_TAG",
281        "reason": "$string:app_name",
282      }
283    ]
284```
285
286```ts
287import { tag } from '@kit.ConnectivityKit';
288import { BusinessError } from '@kit.BasicServicesKit';
289import { hilog } from '@kit.PerformanceAnalysisKit';
290import { AbilityConstant, UIAbility, Want } from '@kit.AbilityKit';
291
292export default class EntryAbility extends UIAbility {
293  onCreate(want: Want, launchParam: AbilityConstant.LaunchParam) {
294    hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onCreate');
295
296    // 获取特定技术类型的NFC标签对象
297    let tagInfo : tag.TagInfo;
298    try {
299      tagInfo = tag.getTagInfo(want);
300    } catch (error) {
301      hilog.error(0x0000, 'testTag', 'getTagInfo error = %{public}s', JSON.stringify(error));
302      return;
303    }
304
305    if (tagInfo == null) {
306      hilog.error(0x0000, 'testTag', 'tagInfo is invalid');
307      return;
308    }
309    if (tagInfo.uid == null) {
310      hilog.error(0x0000, 'testTag', 'uid is invalid');
311      return;
312    }
313    if (tagInfo.technology == null || tagInfo.technology.length == 0) {
314      hilog.error(0x0000, 'testTag', 'technology is invalid');
315      return;
316    }
317
318    // 标签里面可能支持多种技术类型,选择特定的技术类型接口,完成标签数据的读取或写入
319    // 下面示例代码,使用IsoDep完成标签数据的读取或写入
320    let isoDep : tag.IsoDepTag | null = null;
321    for (let i = 0; i < tagInfo.technology.length; i++) {
322      if (tagInfo.technology[i] == tag.ISO_DEP) {
323        try {
324          isoDep = tag.getIsoDep(tagInfo);
325        } catch (error) {
326          hilog.error(0x0000, 'testTag', 'getIsoDep error = %{public}s', JSON.stringify(error));
327          return;
328        }
329      }
330      // 也可以按需选择其它类型的技术读写标签
331    }
332    if (isoDep == null) {
333      hilog.error(0x0000, 'testTag', 'getIsoDep is invalid');
334      return;
335    }
336
337    // 使用IsoDep技术连接到NFC标签
338    try {
339        isoDep.connect();
340    } catch (error) {
341        hilog.error(0x0000, 'testTag', 'isoDep.connect() error = %{public}s', JSON.stringify(error));
342        return;
343    }
344    if (!isoDep.isConnected()) {
345      hilog.error(0x0000, 'testTag', 'isoDep.isConnected() false.');
346      return;
347    }
348
349    // 发送指令到已连接的标签,获取标签的响应数据
350    let cmdData = [0x01, 0x02, 0x03, 0x04]; // 修改为正确的访问标签的指令数据
351    try {
352      isoDep.transmit(cmdData).then((response : number[]) => {
353        hilog.info(0x0000, 'testTag', 'isoDep.transmit() response = %{public}s.', JSON.stringify(response));
354      }).catch((err : BusinessError)=> {
355        hilog.error(0x0000, 'testTag', 'isoDep.transmit() err = %{public}s.', JSON.stringify(err));
356        return;
357      });
358    } catch (businessError) {
359      hilog.error(0x0000, 'testTag', 'isoDep.transmit() businessError = %{public}s.', JSON.stringify(businessError));
360      return;
361    }
362  }
363}
364```