uniapp使用nfc功能及详解

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

 

资料查找

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

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

uniapp使用nfc功能及详解_第1张图片

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

 从启动开始讲起,调用手机自身的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中的真实数据,要从第三个字符开始读取。

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

你可能感兴趣的:(uniapp,JavaScript,Android,uni-app,android,javascript)