前炎
NFC的类型有很多种,详情:android NFC文档
这次的需求是读取NfcV即ISO15693
类型的芯片
本次封装类本体来自uni-app NFC读取卡Id 扇门块的16进制数组 数据解析 (感谢大佬提供的封装)
本次需求原本是读取nfc的mac地址和从NEDF取数据,但后期考虑到NEDF可被读写就改成了读取指定块的数据,下面直接放代码
封装
/utils/NFCServices.js
js
class NFCServices {
readyWriteData = false; //开启写
readyRead = false; //开启读
noNFC = false;
callBack = null;
readyData = ''; //准备写入的数据
static 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']
];
static package_TECH_DISCOVERED = 'android.nfc.action.TECH_DISCOVERED';
static READ_SUCCESS = '1' //读取成功
static READ_FAIL = '2' //读取失败
static READING = '3' //正在读取
static WRITING = '4' //正在写入
static WRITE_SUCCESS = '5' //写入成功
static WRITE_FAIL = '6' //写入失败
static DEVICE_NO_NFC = '7' //设备不支持NFC
static DEVICE_NFC_NOT_ENABLE = '8' //设备未开启NFC
static DEVICE_NFC_NOT_ENABLE = '9' //设备未开启NFC
static INIT = '10' //初始化NFC
static INIT_ERROR = '11' //初始化NFC失败
constructor(callBack, continuousRead) {
//是否连续读取NFC数据
this.isContinuousRead = continuousRead !== undefined ? continuousRead : true
this.callBack = callBack
this.RuntimeMainActivity = plus.android.runtimeMainActivity();
this.Intent = plus.android.importClass('android.content.Intent');
this.PendingIntent = plus.android.importClass('android.app.PendingIntent');
this.IntentFilter = plus.android.importClass('android.content.IntentFilter');
this.NfcAdapter = plus.android.importClass('android.nfc.NfcAdapter');
this.NdefRecord = plus.android.importClass("android.nfc.NdefRecord");
this.NdefMessage = plus.android.importClass("android.nfc.NdefMessage");
this.Ndef = plus.android.importClass('android.nfc.tech.Ndef');
this.NdefFormatable = plus.android.importClass('android.nfc.tech.NdefFormatable');
this.MifareClassic = plus.android.importClass('android.nfc.tech.MifareClassic');
this.nfcA = plus.android.importClass('android.nfc.tech.NfcA')
this.NfcV = plus.android.importClass('android.nfc.tech.NfcV')
// this.Parcelable = plus.android.importClass("android.os.Parcelable");
}
// 将字符串切割成每两个字符一组,然后反转数组,最后拼接成字符串
reverseString(inputString) {
return inputString.match(/.{1,2}/g).reverse().join('');
}
// 字符串转mac
stringToMacAddress(inputString) {
// 如果是真货长度是16位,mac取反
if (inputString.length === 16) {
inputString = this.reverseString(inputString) }
// 删除字符串中的非十六进制字符
var cleanedString = inputString.replace(/[^0-9A-Fa-f]/g, '');
// 将字符串切割成每两个字符一组
var pairs = cleanedString.match(/.{1,2}/g);
// 使用冒号连接数组中的每一组字符
var macAddress = pairs.join(':').toUpperCase();
return macAddress;
}
listenNFCStatus() {
if (uni.getSystemInfoSync().platform !== "android") {
return
}
this.callBack(NFCServices.INIT, '监听NFC状态!')
try {
let nfcAdapter = this.NfcAdapter.getDefaultAdapter(this.RuntimeMainActivity);
if (nfcAdapter == null) {
this.callBack(NFCServices.DEVICE_NO_NFC, '设备不支持NFC!')
this.noNFC = true;
return;
}
if (!nfcAdapter.isEnabled()) {
this.callBack(NFCServices.DEVICE_NFC_NOT_ENABLE, '请在系统设置中先启用NFC功能!')
this.noNFC = true;
return;
} else {
this.noNFC = false;
}
let intent = new this.Intent(this.RuntimeMainActivity, this.RuntimeMainActivity.getClass());
intent.addFlags(this.Intent.FLAG_ACTIVITY_SINGLE_TOP);
let pendingIntent = this.PendingIntent.getActivity(this.RuntimeMainActivity, 0, intent, 0);
let ndef = new this.IntentFilter("android.nfc.action.TECH_DISCOVERED");
ndef.addDataType("*/*");
let intentFiltersArray = [ndef];
const that = this
plus.globalEvent.addEventListener('newintent', () => {
console.log('newintent running');
// 轮询调用 NFC
that.nfcRuning()
// setTimeout(, 1000);
});
plus.globalEvent.addEventListener('pause', (e) => {
console.log('pause running');
if (nfcAdapter) {
//关闭前台调度系统 恢复默认状态
nfcAdapter.disableForegroundDispatch(this.RuntimeMainActivity);
}
});
plus.globalEvent.addEventListener('resume', (e) => {
if (nfcAdapter) {
//开启前台调度系统 优于所有其他NFC
nfcAdapter.enableForegroundDispatch(this.RuntimeMainActivity, pendingIntent,
intentFiltersArray,
NFCServices.techListsArray);
}
});
nfcAdapter.enableForegroundDispatch(this.RuntimeMainActivity, pendingIntent, intentFiltersArray,
NFCServices.techListsArray);
} catch (e) {
this.callBack(NFCServices.INIT_ERROR, '初始化NFC失败!', e)
}
}
nfcRuning() { //
let intent = this.RuntimeMainActivity.getIntent();
if (NFCServices.package_TECH_DISCOVERED == intent.getAction()) {
if (this.isContinuousRead || this.readyRead) {
this.read(intent);
this.readyRead = false;
}
}
}
// 拼接两个块的数据,返回结果是Byte数组或者null
readBlockConcat(nfcV, tagUid, index = 0x24) {
// 0x20: Read Single Block
let buffer = [0x22, 0x20, ...tagUid, index];
return nfcV.transceive(buffer);
}
// 将十六进制字符串每两位分割成一个数组
hexStringToArrayAndConvert(hexString) {
let hexArray = hexString.match(/.{1,2}/g) || [];
// 将每个元素转换为十进制数
let decimalArray = hexArray.map(hex => parseInt(hex, 16));
return decimalArray;
}
// 将每个十进制数值转换为对应的 ASCII 数值
decimalArrayToAscii(decimalArray) {
let asciiArray = decimalArray.map(decimal => String.fromCharCode(decimal));
return asciiArray;
}
read(intent) { // 读代码
this.callBack(NFCServices.READING, '请勿移开标签正在读取数据!')
// NFC id
let bytesId = intent.getByteArrayExtra(this.NfcAdapter.EXTRA_ID);
let nfc_id = this.byteArrayToHexString(bytesId);
let extractedValues = {}
let tag = plus.android.importClass('android.nfc.Tag');
tag = intent.getParcelableExtra(this.NfcAdapter.EXTRA_TAG);
// 开始读取扇区
let NfcV = this.NfcV
// 此处地址有长度判断
if (nfc_id.length === 16) {
let tech = NfcV.get(tag);
if (tech != null) {
try {
tech.connect();
let isConnected = tech.isConnected()
if (isConnected) {
// 感觉这个tagUid就是上面的nfc_id (mac地址)
var tagUid = tag.getId();
//要取0x24 0x25行的数据(36 37)
let line24 = this.readBlockConcat(tech, tagUid, 0x24)
let line25 = this.readBlockConcat(tech, tagUid, 0x25)
if (line24 && line25) {
let str = String(this.byteArrayToHexString(line24) + this.byteArrayToHexString(line25)).slice(4);
// 转换为 ASCII 数组,然后连接成字符串
let statusArr = this.decimalArrayToAscii(this.hexStringToArrayAndConvert(str));
let openStatus = statusArr.length ? statusArr.join('') : 'EEEEEEEE';
extractedValues = {
read_raw: nfc_id,
mac_raw: this.reverseString(nfc_id),
mac: this.stringToMacAddress(nfc_id),
openStatus: openStatus
}
// console.log(extractedValues);
this.callBack(NFCServices.READ_SUCCESS, '读取数据成功!', extractedValues)
}
}
} catch (e) {
console.log('nfc catch', e);
this.callBack(NFCServices.READ_FAIL, '没有读取到数据!')
}
}
} else {
//mac地址长度不符合本次需求,返回mac地址和一个错误的openStatus,openStatus看自己业务情况,可以去掉
extractedValues = {
read_raw: nfc_id,
mac_raw: this.reverseString(nfc_id),
mac: this.stringToMacAddress(nfc_id),
openStatus: 'EEEEEEEE'
}
this.callBack(NFCServices.READ_SUCCESS, '读取数据成功!', extractedValues)
// console.log(extractedValues);
}
// 以下注释为NEDF(payload message)
// if (nfc_id) {
// let Parcelable = plus.android.importClass("android.os.Parcelable");
// let rawmsgs = intent.getParcelableArrayExtra("android.nfc.extra.NDEF_MESSAGES");
// //let rawmsgs = intent.getParcelableArrayExtra();
// if (rawmsgs != null && rawmsgs.length > 0) {
// let records = rawmsgs[0].getRecords();
// let result = records[0].getPayload();
// let data = plus.android.newObject("java.lang.String", result);
// // 因为此NFC芯片 nedf数据 是个网址,读取多了个\u0004,按自己情况处理
// let info = data.replace('\u0004', '')
// let extractedValues = {
// data:info ,
// mac:this.stringToMacAddress(nfc_id),
// }
// // callBack数据按照自己需求来返回
// this.callBack(NFCServices.READ_SUCCESS, '读取数据成功!',extractedValues)
// } else {
// console.log('没有读取到数据');
// this.callBack(NFCServices.READ_FAIL, '没有读取到数据!')
// }
// } else {
// this.callBack(NFCServices.READ_FAIL, '没有读取到数据!')
// }
return nfc_id
}
// 将字节数组转换为字符串
byteArrayToHexString(inarray) {
let i, j, inn;
let hex = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F"];
let out = "";
for (j = 0; j < inarray.length; ++j) {
inn = inarray[j] & 0xff;
i = (inn >>> 4) & 0x0f;
out += hex[i];
i = inn & 0x0f;
out += hex[i];
}
return out;
}
readData() { // 更改读状态
if (this.noNFC) {
this.callBack(NFCServices.READ_FAIL, '请检查设备是否支持并开启 NFC 功能!')
return;
}
// 轮询条件
this.readyRead = true;
this.callBack(NFCServices.READING, '请将NFC标签靠近!')
}
// 设置数据
destroy() {
console.log('destroy');
plus.globalEvent.removeEventListener('newintent', false);
plus.globalEvent.removeEventListener('pause', false);
plus.globalEvent.removeEventListener('resume', false);
}
}
export default NFCServices
附扇区数据截图:
安卓端可以使用 NFC标签助手@1.3.4 [com.fm.nfctools]
以及NFC Tools@8.10 [com.wakdev.wdnfc]
读取NFC信息
下载地址:https://wwm.lanzn.com/b05exoigb 密码:aiwp或者扫码
使用
/pages/out-scan/index.vue
html
<template>
<view>
<view @click="read">读取NFC数据</view>
</view>
</template>
<script>
import NFCServices from '@/utils/nfcServices.js'
let nfcServices;
export default {
data() {
return {}
},
onLoad() {
this.initNfc()
},
onUnload() {
nfcServices&&nfcServices.destroy()
},
methods: {
NFCServicesCallBack(status, tip, data) {
switch (status) {
case NFCServices.READ_SUCCESS:
// 成功回调,就是callback里写的对象,如果是NEDF则是callback里面定义的结构数据
console.log(data)
break;
case NFCServices.READ_FAIL:
// 错误回调
console.error('错误回调',data)
break;
default:
break;
}
},
initNfc() {
nfcServices = new NFCServices(this.NFCServicesCallBack)
},
// 取消NFC监听
cancel() {
// console.log('cancel');
nfcServices.destroy()
nfcServices = null
},
read() {
if (!nfcServices) {
nfcServices = new NFCServices(this.NFCServicesCallBack)
nfcServices.listenNFCStatus()
}else{
nfcServices.listenNFCStatus()
}
uni.showToast({
title: "请将NFC标签靠近",
icon: "none"
})
// 调用 js 文件里面的方法
nfcServices.readData()
},
}
}
</script>
注意:
getid()方法在android.nfc.Tag里,不在NfcV的文档里面