在上一篇中,简单的介绍了下nfc的相关知识,这一篇将以代码的形式更深入的理解nfc。
一、准备工作
首先肯定要申请相应的权限:
同时要注意最小api的版本,根据谷歌官方的说话,NFC在Android上,也是从API9才开始支持的,但是到了API14 Google才对NFC大力开发,所以等到了API15的时候,NFC的传输速度就得到了很大的加强,所以最小api最好设置为14.
然后在相应的要接收nfc信息的activity中注册intent filter:
如果您的活动过滤器是ACTION_TECH_DISCOVERED,你必须创建一个指定的活动支持内技术的XML资源文件,可以通过android.nfc.Tag类的getTechList()获取子集
其中名为nfc_tech_filter的xml文件如下:
android.nfc.tech.IsoDep
android.nfc.tech.NfcA
android.nfc.tech.NfcB
android.nfc.tech.NfcF
android.nfc.tech.NfcV
android.nfc.tech.Ndef
android.nfc.tech.NdefFormatable
android.nfc.tech.MifareClassic
android.nfc.tech.MifareUltralight
当然您也可以指定多个tech-list组。每个的tech-list集独立地考虑,并且您的活动被认为是一个匹配,如果任何一个 tech-list组是由返回的技术的一个子集getTechList(),这提供了AND与OR匹配技术,语义。
下面的例子相匹配,可以支持NFCA和NDEF技术或可以支持NfcB和NDEF技术代码:
android.nfc.tech.NfcA
android.nfc.tech.Ndef
android.nfc.tech.NfcB
android.nfc.tech.Ndef
二、读取数据
做了这么多,终于可以在activity里面读取,写入nfc了,首先我们定义了NfcScanActivity来扫描nfc设备
......//关键代码,onCreate中
// 获取默认的NFC控制器
mAdapter = NfcAdapter.getDefaultAdapter(this);
if (mAdapter == null) {
promt.setText("设备不支持NFC!");
return;
}
if (!mAdapter.isEnabled()) {
promt.setText("请在系统设置中先启用NFC功能!");
return;
}
//创建intent检测nfc
mPendingIntent = PendingIntent
.getActivity(this, 0, new Intent(this, getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);
//onResume
if (this.mAdapter == null)
return;
if (!this.mAdapter.isEnabled()) {
promt.setText("请在系统设置中先启用NFC功能!");
}
//监听nfc设备
this.mAdapter.enableForegroundDispatch(this, this.mPendingIntent, null, null);
因为我们注册了Intent Filter,当扫描到设备后,系统会调用我们的app,进而会进入activity的onNewIntent(Intent paramIntent)方法
@Override
public void onNewIntent(Intent intent) {
setIntent(paramIntent);
//我们接受到消息啦,可以处理了
// 得到是否检测到TAG触发
if (NfcAdapter.ACTION_TECH_DISCOVERED.equals(intent.getAction())
|| NfcAdapter.ACTION_TAG_DISCOVERED.equals(intent.getAction())
|| NfcAdapter.ACTION_NDEF_DISCOVERED.equals(intent.getAction())) {
// 处理该intent
Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
// 获取标签id数组
byte[] bytesId = tag.getId();
//获取消息内容
NfcMessageParser nfcMessageParser = new NfcMessageParser(intent);
List tagMessage = nfcMessageParser.getTagMessage();
if (tagMessage == null || tagMessage.size() == 0) {
//Toast.makeText(this, "NFC格式不支持...", Toast.LENGTH_LONG).show();
} else {
for (int i = 0; i < tagMessage.size(); i++) {
Log.e("tag", tagMessage.get(i));
}
datas = tagMessage.get(0);
}
String info = "";
if (datas != null) {
info += "内容:" + datas + "\n卡片ID:" + bytesToHexString(bytesId) + "\n";
} else {
info += "内容:空" + "\n卡片ID:" + bytesToHexString(bytesId) + "\n";
}
String[] techList = tag.getTechList();
//分析NFC卡的类型: Mifare Classic/UltraLight Info
String cardType = "";
for (String aTechList : techList) {
if (TextUtils.equals(aTechList, "android.nfc.tech.Ndef")) {
Ndef ndef = Ndef.get(tag);
cardType += "最大数据尺寸:" + ndef.getMaxSize() + "字节";
}
}
info += cardType;
promt.setText("NFC信息如下:\n" + info);
}
}
传来的intent中包含了:
EXTRA_TAG:一个Tag对象,表示扫描标签。
EXTRA_NDEF_MESSAGES(可选):从标签解析NDEF消息的数组,这个就是我们要的数据,显示到屏幕上。
EXTRA_ID(可选):标签的ID。
处理消息的代码如下:
// 得到Intent中的NDEF数据
private NdefMessage[] getTagNdef(Intent intent) {
// TODO Auto-generated method stub
NdefMessage[] msgs = null;
Parcelable[] rawMsgs = intent
.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);
//把序列化数据转成Messaeg对象
if (rawMsgs != null) {
msgs = new NdefMessage[rawMsgs.length];
for (int i = 0; i < rawMsgs.length; i++) {
msgs[i] = (NdefMessage) rawMsgs[i];
}
} else {
// Unknown tag type
byte[] empty = new byte[]{};
NdefRecord record = new NdefRecord(NdefRecord.TNF_UNKNOWN, empty,
empty, empty);
NdefMessage msg = new NdefMessage(new NdefRecord[]{record});
msgs = new NdefMessage[]{msg};
}
return msgs;
}
// 把Message转成List
private List getNdefString(NdefMessage[] msgs) {
// TODO Auto-generated method stub
if (msgs != null && msgs.length != 0) {
List tagMessage = parser(msgs[0]);
return tagMessage;
}
return null;
}
然后就可以显示到你想要的地方去了
三、写入数据
写入数据最关键的就是创建一个NdefRecord对象,然后通过Ndef对象的writeNdefMessage(NdefMessage message)方法写入,当然前提还是要检测到设备,这里格式是TNF_WELL_KNOWN with RTD_TEXT,即写入文本字符串,如果你想写入其他数据,请参考官方文档
/**
* 创建record,格式为TNF_WELL_KNOWN with RTD_TEXT
*
* @param payload 你要写入的数据
* @param locale
* @param encodeInUtf8 编码
* @return
*/
public NdefRecord createTextRecord(String payload, Locale locale, boolean encodeInUtf8) {
byte[] langBytes = locale.getLanguage().getBytes(Charset.forName("US-ASCII"));
Charset utfEncoding = encodeInUtf8 ? Charset.forName("UTF-8") : Charset.forName("UTF-16");
byte[] textBytes = payload.getBytes(utfEncoding);
int utfBit = encodeInUtf8 ? 0 : (1 << 7);
char status = (char) (utfBit + langBytes.length);
byte[] data = new byte[1 + langBytes.length + textBytes.length];
data[0] = (byte) status;
System.arraycopy(langBytes, 0, data, 1, langBytes.length);
System.arraycopy(textBytes, 0, data, 1 + langBytes.length, textBytes.length);
NdefRecord record = new NdefRecord(NdefRecord.TNF_WELL_KNOWN,
NdefRecord.RTD_TEXT, new byte[0], data);
return record;
}
生成需要的NdefMessage对象
//textRecord就是上面生成的NdefRecord
NdefMessage message = new NdefMessage(new NdefRecord[]{textRecord});
最后写入数据:
/**
* 写入数据
* @param message
* @param tag intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
* @return
*/
boolean writeTag(NdefMessage message, Tag tag) {
int size = message.toByteArray().length;
try {
//链接nfc
Ndef ndef = Ndef.get(tag);
if (ndef != null) {
ndef.connect();
if (!ndef.isWritable()) {
Toast.makeText(this, "tag不允许写入", Toast.LENGTH_SHORT).show();
return false;
}
if (ndef.getMaxSize() < size) {
Toast.makeText(this,"文件大小超出容量", Toast.LENGTH_SHORT).show();
return false;
}
ndef.writeNdefMessage(message);
Toast.makeText(this,"写入数据成功.", Toast.LENGTH_SHORT).show();
isWrite = false;
finish();
return true;
} else {
NdefFormatable format = NdefFormatable.get(tag);
if (format != null) {
try {
format.connect();
format.format(message);
Toast.makeText(this,"格式化tag并且写入message", Toast.LENGTH_SHORT).show();
return true;
} catch (IOException e) {
Toast.makeText(this, "格式化tag失败.", Toast.LENGTH_SHORT).show();
return false;
}
} else {
Toast.makeText(this, "Tag不支持NDEF", Toast.LENGTH_SHORT).show();
return false;
}
}
} catch (Exception e) {
Toast.makeText(this,"写入数据失败", Toast.LENGTH_SHORT).show();
}
return false;
}