上次记录NFC知识时,还处在研究状态,现在项目的第一阶段开发已经完成。上篇Android之NFC开发,简单介绍了一些知识,也是对未知信息的研究,总要了解一点来龙去脉,省的心发慌。这篇文章总结自己的项目中遇到的问题,和实现基本的NFC读写操作,可以满足一般性的开发需求。
1、首先:在AndroidManifest.xml配置文件里增加NFC权限
<uses-permission android:name="android.permission.NFC" />
<uses-feature android:name="android.hardware.nfc" android:required="true"/>
2.NFC感应操作由NFC适配器来进行处理的:
编写在Activity中onCreate代码:
private NfcAdapter mNfcAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mNfcAdapter = NfcAdapter.getDefaultAdapter(getApplicationContext());
if (mNfcAdapter == null) {
Toast.makeText(this, "该设备不支持nfc", Toast.LENGTH_SHORT).show();
finish();
return;
}
if (!mNfcAdapter.isEnabled()) {
startActivity(new Intent("android.settings.NFC_SETTINGS"));
Toast.makeText(this, "设备未开启nfc", Toast.LENGTH_SHORT).show();
}
}
3.在Activity重写onNewIntent()方法:
//最主要的方法,监听到NFC信息
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(intent.getAction())
|| NfcAdapter.ACTION_TECH_DISCOVERED.equals(intent.getAction())
|| NfcAdapter.ACTION_TAG_DISCOVERED.equals(intent.getAction())) {
iTag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
tagInfo = "";
Parcelable[] data = intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);
if (data != null) {
try {
for (int i = 0; i < data.length; i++) {
NdefRecord[] recs = ((NdefMessage) data[i]).getRecords();
for (int j = 0; j < recs.length; j++) {
if ((recs[j].getTnf() == NdefRecord.TNF_WELL_KNOWN && Arrays.equals(recs[j].getType(), NdefRecord.RTD_TEXT))
|| (recs[j].getTnf() == NdefRecord.TNF_WELL_KNOWN && Arrays.equals(recs[j].getType(), NdefRecord.RTD_URI))) {
/*
*读取普通的文本(即NdefRecord.RTD_TEXT)
*或者是网址(即NdefRecord.RTD_URI)
*/
byte[] payload = recs[j].getPayload();
String textEncoding = "UTF-16";
if ((payload[0] & 0200) == 0) {
textEncoding = "UTF-8";
}
int langCodeLen = payload[0] & 0077;
String s = new String(payload, langCodeLen, payload.length - langCodeLen, textEncoding);
if (!"".equals(s) && !"null".equals(s)) {
tagInfo += s;
}
}
}
}
} catch (Exception e) {
Log.e("TagDispatch", e.toString());
}
Toast.makeText(this, "检测到卡,并成功存储信息!", Toast.LENGTH_LONG).show();
}
}
}
4.重写其他方法:
@Override
protected void onPause() {
super.onPause();
mNfcAdapter.disableForegroundDispatch(this);
}
@Override
protected void onResume() {
super.onResume();
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, new Intent(this,
getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);
IntentFilter[] intentFilters = new IntentFilter[]{};
mNfcAdapter.enableForegroundDispatch(this, pendingIntent, intentFilters, null);
}
5.写入NFC卡信息:
/**
* 将普通的文本字符串写入标签中
*
* @param messageText 要写入标签的文本字符串
* @param tag 要写入的标签
* @return
*/
boolean writeTextTag(String messageText, Tag tag) {
//创建NdefMessage对象和NdefRecord对象
NdefMessage ndefMessage = new NdefMessage(new NdefRecord[]{createTextRecord(messageText)});
int size = ndefMessage.toByteArray().length;
try {
Ndef ndef = Ndef.get(tag);
if (ndef != null) {
//允许对标签进行IO操作
ndef.connect();
if (!ndef.isWritable()) {
Toast.makeText(this, "NFC Tag是只读的!", Toast.LENGTH_LONG).show();
return false;
}
if (ndef.getMaxSize() < size) {
Toast.makeText(this, "NFC Tag的空间不足!", Toast.LENGTH_LONG).show();
return false;
}
//向标签写入数据
ndef.writeNdefMessage(ndefMessage);
Toast.makeText(this, "已成功写入数据!", Toast.LENGTH_LONG).show();
return true;
} else {
//获取可以格式化和向标签写入数据NdefFormatable对象
NdefFormatable format = NdefFormatable.get(tag);
//向非NDEF格式或未格式化的标签写入NDEF格式数据
if (format != null) {
try {
//允许对标签进行IO操作
format.connect();
format.format(ndefMessage);
Toast.makeText(this, "已成功写入数据!", Toast.LENGTH_LONG).show();
return true;
} catch (Exception e) {
Toast.makeText(this, "写入NDEF格式数据失败!", Toast.LENGTH_LONG).show();
return false;
}
} else {
Toast.makeText(this, "NFC标签不支持NDEF格式!", Toast.LENGTH_LONG).show();
return false;
}
}
} catch (IOException e) {
e.printStackTrace();
return false;
} catch (FormatException e) {
e.printStackTrace();
return false;
}
}
/**
* 创建一个NdefRecord对象,用于向标签写入普通的文本数据
*
* @param text
* @return
*/
public NdefRecord createTextRecord(String text) {
//生成语言编码的字节数组,中文编码
byte[] langBytes = Locale.CHINA.getLanguage().getBytes(Charset.forName("US-ASCII"));
//将要写入的文本以UTF_8格式进行编码
Charset utfEncoding = Charset.forName("UTF-8");
//由于已经确定文本的格式编码为UTF_8,所以直接将payload的第1个字节的第7位设为0
byte[] textBytes = text.getBytes(utfEncoding);
int utfBit = 0;
//定义和初始化状态字节
char status = (char) (utfBit + langBytes.length);
//创建存储payload的字节数组
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);
//根据前面设置的payload创建NdefRecord对象
NdefRecord record = new NdefRecord(NdefRecord.TNF_WELL_KNOWN, NdefRecord.RTD_TEXT, new byte[0], data);
return record;
}
6.加密
以上代码直接拷贝就可以读写了,但是这是针对没有密码,并且是ACTION_NDEF_DISCOVERED类型的数据。面对ACTION_TECH_DISCOVERED类型数据,上面的读取的方法就不可用了。
以下是读写ACTION_TECH_DISCOVERED类型数据,并且带有密码的代码,一般的初始密码是:
//默认为:(12个F或0)
FFFFFFFFFFFF
//或者
000000000000
//Nfc卡的密码
private byte[] key = {(byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff};
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(intent.getAction())
|| NfcAdapter.ACTION_TECH_DISCOVERED.equals(intent.getAction()) || NfcAdapter.ACTION_TAG_DISCOVERED.equals(intent.getAction())) {
Tag iTag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
MifareClassic mfc = MifareClassic.get(iTag);
try {
if (null != mfc) {
//链接NFC
mfc.connect();
//获取TAG中包含的扇区数
int sectorCount = mfc.getSectorCount();
for (int j = 0; j < sectorCount; j++) {
//使用KeyA验证扇区
auth = mfc.authenticateSectorWithKeyA(j, key);
//验证通过
if (auth) {
//读取第二块内容
byte[] data = mfc.readBlock(2);
//转换为十六进制的字符串
String s = StringUtil.bytesToHexString(data);
if (null != s && !"".equals(s) && !"null".equals(s)&&j == 0) {
tagInfo = s;
}
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
if(!TextUtils.isEmpty(tagInfo)){
}
}
}
7.对于尚未打开APP进行读写的处理,需要注意。