公司使用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相关的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中的真实数据,要从第三个字符开始读取。
除了写入的内容外,前面两个的字符包含了内容的编码格式,每个字符的长度这些信息。
我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div
我有一个Ruby程序,它使用rubyzip压缩XML文件的目录树。gem。我的问题是文件开始变得很重,我想提高压缩级别,因为压缩时间不是问题。我在rubyzipdocumentation中找不到一种为创建的ZIP文件指定压缩级别的方法。有人知道如何更改此设置吗?是否有另一个允许指定压缩级别的Ruby库? 最佳答案 这是我通过查看rubyzip内部创建的代码。level=Zlib::BEST_COMPRESSIONZip::ZipOutputStream.open(zip_file)do|zip|Dir.glob("**/*")d
类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc
很好奇,就使用rubyonrails自动化单元测试而言,你们正在做什么?您是否创建了一个脚本来在cron中运行rake作业并将结果邮寄给您?git中的预提交Hook?只是手动调用?我完全理解测试,但想知道在错误发生之前捕获错误的最佳实践是什么。让我们理所当然地认为测试本身是完美无缺的,并且可以正常工作。下一步是什么以确保他们在正确的时间将可能有害的结果传达给您? 最佳答案 不确定您到底想听什么,但是有几个级别的自动代码库控制:在处理某项功能时,您可以使用类似autotest的内容获得关于哪些有效,哪些无效的即时反馈。要确保您的提
假设我做了一个模块如下:m=Module.newdoclassCendend三个问题:除了对m的引用之外,还有什么方法可以访问C和m中的其他内容?我可以在创建匿名模块后为其命名吗(就像我输入“module...”一样)?如何在使用完匿名模块后将其删除,使其定义的常量不再存在? 最佳答案 三个答案:是的,使用ObjectSpace.此代码使c引用你的类(class)C不引用m:c=nilObjectSpace.each_object{|obj|c=objif(Class===objandobj.name=~/::C$/)}当然这取决于
我正在尝试使用ruby和Savon来使用网络服务。测试服务为http://www.webservicex.net/WS/WSDetails.aspx?WSID=9&CATID=2require'rubygems'require'savon'client=Savon::Client.new"http://www.webservicex.net/stockquote.asmx?WSDL"client.get_quotedo|soap|soap.body={:symbol=>"AAPL"}end返回SOAP异常。检查soap信封,在我看来soap请求没有正确的命名空间。任何人都可以建议我
关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。
我在我的项目目录中完成了compasscreate.和compassinitrails。几个问题:我已将我的.sass文件放在public/stylesheets中。这是放置它们的正确位置吗?当我运行compasswatch时,它不会自动编译这些.sass文件。我必须手动指定文件:compasswatchpublic/stylesheets/myfile.sass等。如何让它自动运行?文件ie.css、print.css和screen.css已放在stylesheets/compiled。如何在编译后不让它们重新出现的情况下删除它们?我自己编译的.sass文件编译成compiled/t
我想将html转换为纯文本。不过,我不想只删除标签,我想智能地保留尽可能多的格式。为插入换行符标签,检测段落并格式化它们等。输入非常简单,通常是格式良好的html(不是整个文档,只是一堆内容,通常没有anchor或图像)。我可以将几个正则表达式放在一起,让我达到80%,但我认为可能有一些现有的解决方案更智能。 最佳答案 首先,不要尝试为此使用正则表达式。很有可能你会想出一个脆弱/脆弱的解决方案,它会随着HTML的变化而崩溃,或者很难管理和维护。您可以使用Nokogiri快速解析HTML并提取文本:require'nokogiri'h
我想为Heroku构建一个Rails3应用程序。他们使用Postgres作为他们的数据库,所以我通过MacPorts安装了postgres9.0。现在我需要一个postgresgem并且共识是出于性能原因你想要pggem。但是我对我得到的错误感到非常困惑当我尝试在rvm下通过geminstall安装pg时。我已经非常明确地指定了所有postgres目录的位置可以找到但仍然无法完成安装:$envARCHFLAGS='-archx86_64'geminstallpg--\--with-pg-config=/opt/local/var/db/postgresql90/defaultdb/po