uniapp使用nfc功能及详解

188 阅读6分钟

资料查找

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

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

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

var NfcAdapter;
var NdefRecord;
var NdefMessage;
var _getCardNo;

export default {
    initNFC() {
        if (uni.getSystemInfoSync().platform == 'android') {
            listenNFCStatus()
        }
    },
    readNFC(callback) {
        if (uni.getSystemInfoSync().platform == 'android') {
            readData(callback);
        }
    },
    closeNFC() {
        if (uni.getSystemInfoSync().platform == 'android') {
            closeReadAndWrite();
        }
    }
}

function listenNFCStatus() {
    try {
        var main = plus.android.runtimeMainActivity();
        var Intent = plus.android.importClass('android.content.Intent');
        var Activity = plus.android.importClass('android.app.Activity');
        var PendingIntent = plus.android.importClass('android.app.PendingIntent');
        var IntentFilter = plus.android.importClass('android.content.IntentFilter');
        NfcAdapter = plus.android.importClass('android.nfc.NfcAdapter');
        var nfcAdapter = NfcAdapter.getDefaultAdapter(main);

        if (nfcAdapter == null) {
            uni.showToast({
                title: '设备不支持NFC!',
                icon: 'none'
            })
            return;
        }

        if (!nfcAdapter.isEnabled()) {
            uni.showToast({
                title: '请在系统设置中先启用NFC功能!',
                icon: 'none'
            });
            return;
        }

        var intent = new Intent(main, main.getClass());
        intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
        var pendingIntent = PendingIntent.getActivity(main, 0, intent, 0);
        var ndef = new IntentFilter("android.nfc.action.TECH_DISCOVERED");
        ndef.addDataType("*/*");
        var intentFiltersArray = [ndef];
        var techListsArray = [            ["android.nfc.tech.IsoDep"],
            ["android.nfc.tech.NfcA"],
            ["android.nfc.tech.NfcB"],
            ["android.nfc.tech.NfcF"],
            ["android.nfc.tech.Nfcf"],
            ["android.nfc.tech.NfcV"],
            ["android.nfc.tech.NdefFormatable"],
            ["android.nfc.tech.MifareClassic"],
            ["android.nfc.tech.MifareUltralight"]
        ];
        plus.globalEvent.addEventListener("newintent",
            function() {
                setTimeout(handle_nfc_data1, 1000);
            }, false);
        plus.globalEvent.addEventListener("pause", function(e) {
            if (nfcAdapter) {
                nfcAdapter.disableForegroundDispatch(main);
            }
        }, false);
        plus.globalEvent.addEventListener("resume", function(e) {
            if (nfcAdapter) {
                //console.log('resume');
                nfcAdapter.enableForegroundDispatch(main, pendingIntent, intentFiltersArray, techListsArray);
            }
        }, false);
        nfcAdapter.enableForegroundDispatch(main, pendingIntent, intentFiltersArray, techListsArray);
    } catch (e) {
        console.error(e);
    }
}

function handle_nfc_data1() {
    NdefRecord = plus.android.importClass("android.nfc.NdefRecord");
    NdefMessage = plus.android.importClass("android.nfc.NdefMessage");
    var main = plus.android.runtimeMainActivity();
    var intent = main.getIntent();
    //console.log("action type:" + intent.getAction());
    if ("android.nfc.action.TECH_DISCOVERED" == intent.getAction()) {
        if (readyWriteData) {
            //__write(intent);
            readyWriteData = false;
        } else if (readyRead) {
            __read(intent);
            readyRead = false;
        }
    }
}

function showToast(msg) {
    plus.nativeUI.toast(msg);
}

// function __write(intent) {
//  try {
//      waiting.setTitle('请勿移开标签\n正在写入...');
//      var text = document.getElementById('text').value;
//      console.log("text=" + text);
//      var textBytes = plus.android.invoke(text, "getBytes");
//      var textRecord = new NdefRecord(NdefRecord.TNF_MIME_MEDIA,
//          plus.android.invoke("text/plain", "getBytes"), plus.android.invoke("", "getBytes"), textBytes);
//      var message = new NdefMessage([textRecord]);
//      var Ndef = plus.android.importClass('android.nfc.tech.Ndef');
//      var NdefFormatable = plus.android.importClass('android.nfc.tech.NdefFormatable');
//      var tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
//      var ndef = Ndef.get(tag);
//      if (ndef != null) {
//          var size = message.toByteArray().length;
//          console.log("size=" + size);
//          ndef.connect();
//          if (!ndef.isWritable()) {
//              showToast("tag不允许写入");
//              waiting.close();
//              return;
//          }
//          if (ndef.getMaxSize() < size) {
//              showToast("文件大小超出容量");
//              waiting.close();
//              return;
//          }

//          ndef.writeNdefMessage(message);
//          waiting.close();
//          showToast("写入数据成功.");
//          return;
//      } else {
//          var format = NdefFormatable.get(tag);
//          if (format != null) {
//              try {
//                  format.connect();
//                  format.format(message);
//                  showToast("格式化tag并且写入message");
//                  waiting.close();
//                  return;
//              } catch (e) {
//                  showToast("格式化tag失败.");
//                  waiting.close();
//                  return;
//              }
//          } else {
//              showToast("Tag不支持NDEF");
//              waiting.close();
//              return;
//          }
//      }
//  } catch (e) {
//      console.log("error=" + e);
//      waiting.close();
//      alert('写入失败');
//  }

// }

function __read(intent) {
    try {
        var content = "";
        waiting.setTitle('请勿移开标签\n正在读取数据...');
        var tag = plus.android.importClass("android.nfc.Tag");
        tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
        var bytesId = intent.getByteArrayExtra(NfcAdapter.EXTRA_ID);
        waiting.close();
        var tagid = bytesToHexString(tag.getId())
        if (typeof _getCardNo === 'function') {
            _getCardNo(tagid);
        }
    } catch (e) {
        uni.showToast({
            title: e,
            icon: 'none'
        });
    }
}

function bytesToHexString(inarray) {
    var i, j, x;
    var hex = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A",        "B", "C", "D", "E", "F"    ];
    var out = "";
    for (j = 0; j < inarray.length; ++j) {
        x = parseInt(inarray[j]) & 0xff;
        i = (x >> 4) & 0x0f;
        out += hex[i];
        i = x & 0x0f;
        out += hex[i];
    }
    return out;
}

function reverseTwo(str) {

    var str1 = "";
    for (var i = 1; i <= str.length; i++) {
        str1 += str[i - 1];
        if (i % 2 == 0) {
            if (i == str.length) {
                break;
            }
            str1 += ":";
        }
    }
    var str2 = "";
    for (var i = str1.split(":").length - 1; i >= 0; i--) {
        str2 += str1.split(":")[i];
    }
    return str2;
}

if (uni.getSystemInfoSync().platform == 'android') {
    //plus.globalEvent.addEventListener('plusready', listenNFCStatus, false);
}

var waiting;
var readyWriteData = false;
var readyRead = false;

function writeData() {
    var textEle = plus.globalEvent.getElementById('text');
    if (!textEle.value) {
        uni.showToast({
            title: '请输入要写入的内容!',
            icon: 'none'
        });
        return;
    }
    readyWriteData = true;
    waiting = plus.nativeUI.showWaiting("请将NFC标签靠近!");
}

function readData(getCardNo) {
    readyRead = true;
    _getCardNo = getCardNo
    waiting = plus.nativeUI.showWaiting("请将NFC标签靠近!", {
        modal: false
    });
}

function closeReadAndWrite() {
    readyWriteData = false;
    readyRead = false;

    if (waiting) {
        waiting.close();
    }
}

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

读取到的 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,读取到数据之后就跳转回来。

        var main = plus.android.runtimeMainActivity();
        var Intent = plus.android.importClass('android.content.Intent');
        var Activity = plus.android.importClass('android.app.Activity');
        var PendingIntent = plus.android.importClass('android.app.PendingIntent');
        var IntentFilter = plus.android.importClass('android.content.IntentFilter');
        NfcAdapter = plus.android.importClass('android.nfc.NfcAdapter');
        var nfcAdapter = NfcAdapter.getDefaultAdapter(main);



。。。。。。。。。。。。。。。。。。。。。。。。。。。。。


        var intent = new Intent(main, main.getClass());
        intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
        var pendingIntent = PendingIntent.getActivity(main, 0, intent, 0);
        var ndef = new IntentFilter("android.nfc.action.TECH_DISCOVERED");
        ndef.addDataType("*/*");
        var intentFiltersArray = [ndef];
        var techListsArray = [
            ["android.nfc.tech.IsoDep"],
            ["android.nfc.tech.NfcA"],
            ["android.nfc.tech.NfcB"],
            ["android.nfc.tech.NfcF"],
            ["android.nfc.tech.Nfcf"],
            ["android.nfc.tech.NfcV"],
            ["android.nfc.tech.NdefFormatable"],
            ["android.nfc.tech.MifareClassic"],
            ["android.nfc.tech.MifareUltralight"]
        ];
        nfcAdapter.enableForegroundDispatch(main, pendingIntent, intentFiltersArray, techListsArray);

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

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

        plus.globalEvent.addEventListener("newintent",
            function() {
                setTimeout(handle_nfc_data1, 1000);
            }, false);
        plus.globalEvent.addEventListener("pause", function(e) {
            if (nfcAdapter) {
                nfcAdapter.disableForegroundDispatch(main);
            }
        }, false);
        plus.globalEvent.addEventListener("resume", function(e) {
            if (nfcAdapter) {
                //console.log('resume');
                nfcAdapter.enableForegroundDispatch(main, pendingIntent, intentFiltersArray, techListsArray);
            }
        }, false);

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

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

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

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

bytesToHexString 将数据转换。

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

function __read(intent) {
    try {
      var content = "";
      waiting.setTitle('正在读取数据...');
      var tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
      var messages = intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);
      console.log(messages)
      for (var i = 0; i < messages.length; i++) {
        var message = messages[i];
        if (message != null) {
          var records =  message.getRecords();

          // Loop through all NDEF records in the current NDEF message
          for (var j = 0; j < records.length; j++) {
            var recordType = String(records[j].getType());
            var payload = String(records[j].getPayload());

            // Decode payload to readable text
            payload = decodeURIComponent(escape(payload));
            // TPIM00010141
            // Print out result
            console.log("Record type: " + recordType);
            // String.fromCharCode(parseInt(payload[i]))
            // var Realpayload = payload.split(',').map(strItem => String.fromCharCode(parseInt(strItem))).join('')
            let textEncoding = (payload[0] & 0x80) === 0 ? "UTF-8" : "UTF-16";
            let languageCodeLength = payload[0] & 0x3F;
            let textStartIndex = 1 + languageCodeLength;
            let text = payload.split(',').slice(textStartIndex).map(strItem => String.fromCharCode(parseInt(strItem))).join('')
            content = text
          }
        }
      }
      waiting.close();
      var tagid = String(content)
      if (typeof _getCardNo === 'function') {
          _getCardNo(tagid);
      }
    } catch (e) {
      console.log(e)
        uni.showToast({
            title: e,
            icon: 'none'
        });
    }
}

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

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


版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

原文链接::blog.csdn.net/GhostPaints…