• uniapp使用nfc功能及详解


    公司使用uniapp在android手机端要增加一个nfc识别的功能。在此记录一下实现的过程。

     

    资料查找

    我的代码逻辑主要来源于找到的这篇文章:

    uniapp-安卓NFC读取 - 我要找到我的全世界 - 博客园

     文章内附有代码,为防止文章失效代码消失,在这篇文章里面也记录一下。

    1. var NfcAdapter;
    2. var NdefRecord;
    3. var NdefMessage;
    4. var _getCardNo;
    5. export default {
    6. initNFC() {
    7. if (uni.getSystemInfoSync().platform == 'android') {
    8. listenNFCStatus()
    9. }
    10. },
    11. readNFC(callback) {
    12. if (uni.getSystemInfoSync().platform == 'android') {
    13. readData(callback);
    14. }
    15. },
    16. closeNFC() {
    17. if (uni.getSystemInfoSync().platform == 'android') {
    18. closeReadAndWrite();
    19. }
    20. }
    21. }
    22. function listenNFCStatus() {
    23. try {
    24. var main = plus.android.runtimeMainActivity();
    25. var Intent = plus.android.importClass('android.content.Intent');
    26. var Activity = plus.android.importClass('android.app.Activity');
    27. var PendingIntent = plus.android.importClass('android.app.PendingIntent');
    28. var IntentFilter = plus.android.importClass('android.content.IntentFilter');
    29. NfcAdapter = plus.android.importClass('android.nfc.NfcAdapter');
    30. var nfcAdapter = NfcAdapter.getDefaultAdapter(main);
    31. if (nfcAdapter == null) {
    32. uni.showToast({
    33. title: '设备不支持NFC!',
    34. icon: 'none'
    35. })
    36. return;
    37. }
    38. if (!nfcAdapter.isEnabled()) {
    39. uni.showToast({
    40. title: '请在系统设置中先启用NFC功能!',
    41. icon: 'none'
    42. });
    43. return;
    44. }
    45. var intent = new Intent(main, main.getClass());
    46. intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
    47. var pendingIntent = PendingIntent.getActivity(main, 0, intent, 0);
    48. var ndef = new IntentFilter("android.nfc.action.TECH_DISCOVERED");
    49. ndef.addDataType("*/*");
    50. var intentFiltersArray = [ndef];
    51. var techListsArray = [
    52. ["android.nfc.tech.IsoDep"],
    53. ["android.nfc.tech.NfcA"],
    54. ["android.nfc.tech.NfcB"],
    55. ["android.nfc.tech.NfcF"],
    56. ["android.nfc.tech.Nfcf"],
    57. ["android.nfc.tech.NfcV"],
    58. ["android.nfc.tech.NdefFormatable"],
    59. ["android.nfc.tech.MifareClassic"],
    60. ["android.nfc.tech.MifareUltralight"]
    61. ];
    62. plus.globalEvent.addEventListener("newintent",
    63. function() {
    64. setTimeout(handle_nfc_data1, 1000);
    65. }, false);
    66. plus.globalEvent.addEventListener("pause", function(e) {
    67. if (nfcAdapter) {
    68. nfcAdapter.disableForegroundDispatch(main);
    69. }
    70. }, false);
    71. plus.globalEvent.addEventListener("resume", function(e) {
    72. if (nfcAdapter) {
    73. //console.log('resume');
    74. nfcAdapter.enableForegroundDispatch(main, pendingIntent, intentFiltersArray, techListsArray);
    75. }
    76. }, false);
    77. nfcAdapter.enableForegroundDispatch(main, pendingIntent, intentFiltersArray, techListsArray);
    78. } catch (e) {
    79. console.error(e);
    80. }
    81. }
    82. function handle_nfc_data1() {
    83. NdefRecord = plus.android.importClass("android.nfc.NdefRecord");
    84. NdefMessage = plus.android.importClass("android.nfc.NdefMessage");
    85. var main = plus.android.runtimeMainActivity();
    86. var intent = main.getIntent();
    87. //console.log("action type:" + intent.getAction());
    88. if ("android.nfc.action.TECH_DISCOVERED" == intent.getAction()) {
    89. if (readyWriteData) {
    90. //__write(intent);
    91. readyWriteData = false;
    92. } else if (readyRead) {
    93. __read(intent);
    94. readyRead = false;
    95. }
    96. }
    97. }
    98. function showToast(msg) {
    99. plus.nativeUI.toast(msg);
    100. }
    101. // function __write(intent) {
    102. // try {
    103. // waiting.setTitle('请勿移开标签\n正在写入...');
    104. // var text = document.getElementById('text').value;
    105. // console.log("text=" + text);
    106. // var textBytes = plus.android.invoke(text, "getBytes");
    107. // var textRecord = new NdefRecord(NdefRecord.TNF_MIME_MEDIA,
    108. // plus.android.invoke("text/plain", "getBytes"), plus.android.invoke("", "getBytes"), textBytes);
    109. // var message = new NdefMessage([textRecord]);
    110. // var Ndef = plus.android.importClass('android.nfc.tech.Ndef');
    111. // var NdefFormatable = plus.android.importClass('android.nfc.tech.NdefFormatable');
    112. // var tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
    113. // var ndef = Ndef.get(tag);
    114. // if (ndef != null) {
    115. // var size = message.toByteArray().length;
    116. // console.log("size=" + size);
    117. // ndef.connect();
    118. // if (!ndef.isWritable()) {
    119. // showToast("tag不允许写入");
    120. // waiting.close();
    121. // return;
    122. // }
    123. // if (ndef.getMaxSize() < size) {
    124. // showToast("文件大小超出容量");
    125. // waiting.close();
    126. // return;
    127. // }
    128. // ndef.writeNdefMessage(message);
    129. // waiting.close();
    130. // showToast("写入数据成功.");
    131. // return;
    132. // } else {
    133. // var format = NdefFormatable.get(tag);
    134. // if (format != null) {
    135. // try {
    136. // format.connect();
    137. // format.format(message);
    138. // showToast("格式化tag并且写入message");
    139. // waiting.close();
    140. // return;
    141. // } catch (e) {
    142. // showToast("格式化tag失败.");
    143. // waiting.close();
    144. // return;
    145. // }
    146. // } else {
    147. // showToast("Tag不支持NDEF");
    148. // waiting.close();
    149. // return;
    150. // }
    151. // }
    152. // } catch (e) {
    153. // console.log("error=" + e);
    154. // waiting.close();
    155. // alert('写入失败');
    156. // }
    157. // }
    158. function __read(intent) {
    159. try {
    160. var content = "";
    161. waiting.setTitle('请勿移开标签\n正在读取数据...');
    162. var tag = plus.android.importClass("android.nfc.Tag");
    163. tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
    164. var bytesId = intent.getByteArrayExtra(NfcAdapter.EXTRA_ID);
    165. waiting.close();
    166. var tagid = bytesToHexString(tag.getId())
    167. if (typeof _getCardNo === 'function') {
    168. _getCardNo(tagid);
    169. }
    170. } catch (e) {
    171. uni.showToast({
    172. title: e,
    173. icon: 'none'
    174. });
    175. }
    176. }
    177. function bytesToHexString(inarray) {
    178. var i, j, x;
    179. var hex = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A",
    180. "B", "C", "D", "E", "F"
    181. ];
    182. var out = "";
    183. for (j = 0; j < inarray.length; ++j) {
    184. x = parseInt(inarray[j]) & 0xff;
    185. i = (x >> 4) & 0x0f;
    186. out += hex[i];
    187. i = x & 0x0f;
    188. out += hex[i];
    189. }
    190. return out;
    191. }
    192. function reverseTwo(str) {
    193. var str1 = "";
    194. for (var i = 1; i <= str.length; i++) {
    195. str1 += str[i - 1];
    196. if (i % 2 == 0) {
    197. if (i == str.length) {
    198. break;
    199. }
    200. str1 += ":";
    201. }
    202. }
    203. var str2 = "";
    204. for (var i = str1.split(":").length - 1; i >= 0; i--) {
    205. str2 += str1.split(":")[i];
    206. }
    207. return str2;
    208. }
    209. if (uni.getSystemInfoSync().platform == 'android') {
    210. //plus.globalEvent.addEventListener('plusready', listenNFCStatus, false);
    211. }
    212. var waiting;
    213. var readyWriteData = false;
    214. var readyRead = false;
    215. function writeData() {
    216. var textEle = plus.globalEvent.getElementById('text');
    217. if (!textEle.value) {
    218. uni.showToast({
    219. title: '请输入要写入的内容!',
    220. icon: 'none'
    221. });
    222. return;
    223. }
    224. readyWriteData = true;
    225. waiting = plus.nativeUI.showWaiting("请将NFC标签靠近!");
    226. }
    227. function readData(getCardNo) {
    228. readyRead = true;
    229. _getCardNo = getCardNo
    230. waiting = plus.nativeUI.showWaiting("请将NFC标签靠近!", {
    231. modal: false
    232. });
    233. }
    234. function closeReadAndWrite() {
    235. readyWriteData = false;
    236. readyRead = false;
    237. if (waiting) {
    238. waiting.close();
    239. }
    240. }

    代码内部是存在着写入功能的,被注释掉了,单单使用读取功能。

    读取到的NFC类型有比较多种,这是我找到的比较好的一篇解释,大部分文章都有提到的4种类型。

    搞懂Nfc刷卡看这篇就够了 - 知乎

    文章里面附带了NFC相关的android官方文档,因为解析代码逻辑需要用到,我就把官网的链接也贴在下面。

    NFC 基础知识  |  Android 开发者  |  Android Developers  

    NfcAdapter  |  Android Developers

    代码使用:

     第一步:新建一个js文件,复制代码进去。

    第二步:在涉及到使用nfc识别的页面,引入这个js文件。主要三个方法:初始化,关闭识别,识别成功回调方法

    onload里面执行初始化initNFC,在离开页面的时候closeNFC(博主本人放在unload),点击某个按钮BUTTON的时候开始识别readNFC。

    开启识别的时候会一直等待识别,建议添加一个setTimeout定时器,在固定时间调用closeNFC关闭识别,点击BUTTON的时候先清除再调用,识别成功的回调方法里面也要清理这个定时器。

    代码逻辑个人理解:

    uniapp需要在manifest.json中开启手机的NFC识别权限。

    代码中也添加了判断手机是否开启了NFC识别,NFC的识别功能是调用了手机自身的一个intent,然后跳转回来。

     NfcAdapter适配器里面包含了许多的识别到的信息,这些信息都是要经过序列化才能使用的,如果不确定该调用哪个序列化方法,或者想知道还有哪些方法,可以去官方文档 里面找。

    代码中就有使用到这两个值。

     从启动开始讲起,调用手机自身的nfc识别,就意味着当前的intent跳转到另外一个intent,读取到数据之后就跳转回来。

    1. var main = plus.android.runtimeMainActivity();
    2. var Intent = plus.android.importClass('android.content.Intent');
    3. var Activity = plus.android.importClass('android.app.Activity');
    4. var PendingIntent = plus.android.importClass('android.app.PendingIntent');
    5. var IntentFilter = plus.android.importClass('android.content.IntentFilter');
    6. NfcAdapter = plus.android.importClass('android.nfc.NfcAdapter');
    7. var nfcAdapter = NfcAdapter.getDefaultAdapter(main);
    8. 。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
    9. var intent = new Intent(main, main.getClass());
    10. intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
    11. var pendingIntent = PendingIntent.getActivity(main, 0, intent, 0);
    12. var ndef = new IntentFilter("android.nfc.action.TECH_DISCOVERED");
    13. ndef.addDataType("*/*");
    14. var intentFiltersArray = [ndef];
    15. var techListsArray = [
    16. ["android.nfc.tech.IsoDep"],
    17. ["android.nfc.tech.NfcA"],
    18. ["android.nfc.tech.NfcB"],
    19. ["android.nfc.tech.NfcF"],
    20. ["android.nfc.tech.Nfcf"],
    21. ["android.nfc.tech.NfcV"],
    22. ["android.nfc.tech.NdefFormatable"],
    23. ["android.nfc.tech.MifareClassic"],
    24. ["android.nfc.tech.MifareUltralight"]
    25. ];
    26. nfcAdapter.enableForegroundDispatch(main, pendingIntent, intentFiltersArray, techListsArray);

    这些就是一些跳转的设置,下半部分设置了跳转的启动方式为Intent.FLAG_ACTIVITY_SINGLE_TOP,并且设置了intent过滤器。

    下一步就是监听这些跳转,并处理放回的数据。于是在代码中可以看到下面的这些监听方法。

    1. plus.globalEvent.addEventListener("newintent",
    2. function() {
    3. setTimeout(handle_nfc_data1, 1000);
    4. }, false);
    5. plus.globalEvent.addEventListener("pause", function(e) {
    6. if (nfcAdapter) {
    7. nfcAdapter.disableForegroundDispatch(main);
    8. }
    9. }, false);
    10. plus.globalEvent.addEventListener("resume", function(e) {
    11. if (nfcAdapter) {
    12. //console.log('resume');
    13. nfcAdapter.enableForegroundDispatch(main, pendingIntent, intentFiltersArray, techListsArray);
    14. }
    15. }, false);

    主要是“newintent"的监听,里面实现了主要的逻辑。

    手机识别到nfc跳转回到应用的时候,intent会携带返回的信息。进行判断是否是"android.nfc.action.TECH_DISCOVERED"。意味着识别到了信息。

    __read方法就是主要的处理信息的内容了,也是最重要的。

    intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);将数据进行序列化。应用与应用直接传递数据,都要进行序列化和反序列化处理的。

    bytesToHexString将数据转换。

    上面内容是读取NFC标签卡的TAG标签ID值,如果是要读取NFC写入的MESSAGE信息。使用下面的方法进行替换。

    1. function __read(intent) {
    2. try {
    3. var content = "";
    4. waiting.setTitle('正在读取数据...');
    5. var tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
    6. var messages = intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);
    7. console.log(messages)
    8. for (var i = 0; i < messages.length; i++) {
    9. var message = messages[i];
    10. if (message != null) {
    11. var records = message.getRecords();
    12. // Loop through all NDEF records in the current NDEF message
    13. for (var j = 0; j < records.length; j++) {
    14. var recordType = String(records[j].getType());
    15. var payload = String(records[j].getPayload());
    16. // Decode payload to readable text
    17. payload = decodeURIComponent(escape(payload));
    18. // TPIM00010141
    19. // Print out result
    20. console.log("Record type: " + recordType);
    21. // String.fromCharCode(parseInt(payload[i]))
    22. // var Realpayload = payload.split(',').map(strItem => String.fromCharCode(parseInt(strItem))).join('')
    23. let textEncoding = (payload[0] & 0x80) === 0 ? "UTF-8" : "UTF-16";
    24. let languageCodeLength = payload[0] & 0x3F;
    25. let textStartIndex = 1 + languageCodeLength;
    26. let text = payload.split(',').slice(textStartIndex).map(strItem => String.fromCharCode(parseInt(strItem))).join('')
    27. content = text
    28. }
    29. }
    30. }
    31. waiting.close();
    32. var tagid = String(content)
    33. if (typeof _getCardNo === 'function') {
    34. _getCardNo(tagid);
    35. }
    36. } catch (e) {
    37. console.log(e)
    38. uni.showToast({
    39. title: e,
    40. icon: 'none'
    41. });
    42. }
    43. }

    NFC的EXTRA_NDEF_MESSAGES有一个写入规则,payload中的真实数据,要从第三个字符开始读取。

    除了写入的内容外,前面两个的字符包含了内容的编码格式,每个字符的长度这些信息。

  • 相关阅读:
    【自然语言处理】利用python创建简单的聊天系统
    MyBatis 映射文件(Mapper XML):配置与使用
    UNITY与安卓⭐二、AndroidStudio中关于通讯的使用教学
    Java 第二阶段提升编程能力【常用类】
    【洛谷 P5730】【深基5.例10】显示屏 题解(数组+循环)
    浅谈etcd服务注册与发现
    了解前端知识
    全网最牛自动化测试框架系列之pytest(6)-Fixture(固件)
    vue3 globalData 的使用方法
    数字藏品和ICP
  • 原文地址:https://blog.csdn.net/GhostPaints/article/details/127967951