android nfc ndef mifareclassic,Android NFC开发-实践篇
Android NFC開發(fā)-實踐篇
https://blog..net/_GYG/article/details/72899417
在Android NFC開發(fā)-理論篇中,我們了解了在Android中開發(fā)NFC的一些理論知識,這篇我們繼續(xù)應用我們上一篇學到的知識,實現(xiàn)對NDEF格式標簽和MifareClassic格式標簽的讀寫操作。
基本操作
配置AndroidMenifest.xml:
獲取設備默認的NfcAdapter對象,判斷該設備是否支持NFC功能,若支持,判斷此功能是否打開,并且創(chuàng)建一個PendingIntent對象,用于當NFC標簽被檢測到時,啟動我們處理NFC標簽的Activity
@Override
protected void onStart() {
super.onStart();
mNfcAdapter= NfcAdapter.getDefaultAdapter(this);//設備的NfcAdapter對象
if(mNfcAdapter==null){//判斷設備是否支持NFC功能
Toast.makeText(this,"設備不支持NFC功能!",Toast.LENGTH_SHORT);
finish();
return;
}
if (!mNfcAdapter.isEnabled()){//判斷設備NFC功能是否打開
Toast.makeText(this,"請到系統(tǒng)設置中打開NFC功能!",Toast.LENGTH_SHORT);
finish();
return;
}
mPendingIntent=PendingIntent.getActivity(this,0,new Intent(this,getClass()),0);//創(chuàng)建PendingIntent對象,當檢測到一個Tag標簽就會執(zhí)行此Intent
}
在OnNewIntent()方法中,獲取到Tag對象
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
mTag=intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);//獲取到Tag標簽對象
String[] techList=mTag.getTechList();
System.out.println("標簽支持的tachnology類型:");
for (String tech:techList){
System.out.println(tech);
}
}
為了更好的處理NFC標簽,我們需要在Activity獲取焦點時(onResume),在主線程中啟動前臺發(fā)布系統(tǒng),并且在Activity失去焦點時,關閉前臺發(fā)布系統(tǒng)
//頁面獲取焦點
@Override
protected void onResume() {
super.onResume();
if (mNfcAdapter!=null){ mNfcAdapter.enableForegroundDispatch(this,mPendingIntent,null,null);//打開前臺發(fā)布系統(tǒng),使頁面優(yōu)于其它nfc處理.當檢測到一個Tag標簽就會執(zhí)行mPendingItent
}
}
//頁面失去焦點
@Override
protected void onPause() {
super.onPause();
if(mNfcAdapter!=null){
mNfcAdapter.disableForegroundDispatch(this);//關閉前臺發(fā)布系統(tǒng)
}
}
以上所有操作,都是對一個NFC標簽的基本操作,我們封裝在一個BaseNfcActivity中,對不同格式標簽讀寫的Activity都繼承BaseNfcActivity。
NDEF格式標簽讀寫
我們可以通過Tag對象的getTechList()獲取到標簽的技術(shù)類型,只有支持NDEF格式的標簽才可以進行NDEF格式標簽的讀寫操作。
讀寫NDEF格式標簽主要涉及到兩個類:
NdefMessage:描述NDEF格式的信息,實際上我們寫入NFC標簽的就是NdefMessage對象。
NdefRecord:描述NDEF信息的一個信息段,一個NdefMessage可能包含一個或者多個NdefRecord。
獲取Ndef對象
Ndef ndef=Ndef.get(mTag);//獲取ndef對象
創(chuàng)建NdefRecord,Android為我們提供了創(chuàng)建NdefRecord的方法,是我們可以輕松創(chuàng)建一個NdefRecord對象
NdefRecord.createApplicationRecord(String packageName)
NdefRecord.createUri(Uri uri)
NdefRecord.createUri(String uriString)
NdefRecord.createTextRecord(String languageCode, String text)
遺憾的是NdefRecord.createTextRecord(String languageCode, String text)最小兼容sdk版本是21,對于需要兼容更小版本的應用來說就需要我們自己來實現(xiàn)這個方法。
不管什么格式的數(shù)據(jù)本質(zhì)上都是由一些字節(jié)組成的。對于NDEF文本格式來說,這些數(shù)據(jù)的第1個字節(jié)描述了數(shù)據(jù)的狀態(tài),然后若干個字節(jié)描述文本的語言編碼,最后剩余字節(jié)表示文本數(shù)據(jù)。這些數(shù)據(jù)格式由NFC Forum的相關規(guī)范定義,可以通過?http://members.nfc-forum.org/specs/spec_dashboard?下載相關的規(guī)范。
NDEF的文本數(shù)據(jù)規(guī)范:
偏移量
長度(bytes)
描述
0
1
狀態(tài)字節(jié),見下表(狀態(tài)字節(jié)編碼格式)
1
n
ISO/IANA語言編碼。例如”en-US”,”fr-CA”。編碼格式是US-ASCII,長度(n)由狀態(tài)字節(jié)的后6位指定。
n+1
m
文本數(shù)據(jù)。編碼格式是UTF-8或UTF-16。編碼格式由狀態(tài)字節(jié)的前3位指定。
狀態(tài)字節(jié)編碼格式:
字節(jié)位(0是最低位,7是最高位)
含義
7
0:文本編碼為UTF-8,1:文本編碼為UTF-16
6
必須設為0
5..0
語言編碼的長度(占用的字節(jié)個數(shù))
創(chuàng)建文本NdefRecord
/**
* 創(chuàng)建NDEF文本數(shù)據(jù)
* @param text
* @return
*/
public static NdefRecord createTextRecord(String text) {
byte[] langBytes = Locale.CHINA.getLanguage().getBytes(Charset.forName("US-ASCII"));
Charset utfEncoding = Charset.forName("UTF-8");
//將文本轉(zhuǎn)換為UTF-8格式
byte[] textBytes = text.getBytes(utfEncoding);
//設置狀態(tài)字節(jié)編碼最高位數(shù)為0
int utfBit = 0;
//定義狀態(tài)字節(jié)
char status = (char) (utfBit + langBytes.length);
byte[] data = new byte[1 + langBytes.length + textBytes.length];
//設置第一個狀態(tài)字節(jié),先將狀態(tài)碼轉(zhuǎn)換成字節(jié)
data[0] = (byte) status;
//設置語言編碼,使用數(shù)組拷貝方法,從0開始拷貝到data中,拷貝到data的1到langBytes.length的位置
System.arraycopy(langBytes, 0, data, 1, langBytes.length);
//設置文本字節(jié),使用數(shù)組拷貝方法,從0開始拷貝到data中,拷貝到data的1 + langBytes.length
//到textBytes.length的位置
System.arraycopy(textBytes, 0, data, 1 + langBytes.length, textBytes.length);
//通過字節(jié)傳入NdefRecord對象
//NdefRecord.RTD_TEXT:傳入類型 讀寫
NdefRecord ndefRecord = new NdefRecord(NdefRecord.TNF_WELL_KNOWN,
NdefRecord.RTD_TEXT, new byte[0], data);
return ndefRecord;
}
創(chuàng)建NdefMessage,并且寫入Ndef標簽
//往Ndef標簽中寫數(shù)據(jù)
private void writeNdef(){
if (mTag==null){
Toast.makeText(this,"不能識別的標簽類型!",Toast.LENGTH_SHORT);
finish();
return;
}
Ndef ndef=Ndef.get(mTag);//獲取ndef對象
if (!ndef.isWritable()){
Toast.makeText(this,"該標簽不能寫入數(shù)據(jù)!",Toast.LENGTH_SHORT);
return;
}
NdefRecord ndefRecord=createTextRecord(writeEdt.getText().toString());//創(chuàng)建一個NdefRecord對象
NdefMessage ndefMessage=new NdefMessage(new NdefRecord[]{ndefRecord});//根據(jù)NdefRecord數(shù)組,創(chuàng)建一個NdefMessage對象
int size=ndefMessage.getByteArrayLength();
if (ndef.getMaxSize()
Toast.makeText(this,"標簽容量不足!",Toast.LENGTH_SHORT);
return;
}
try {
ndef.connect();//連接
ndef.writeNdefMessage(ndefMessage);//寫數(shù)據(jù)
Toast.makeText(this,"數(shù)據(jù)寫入成功!",Toast.LENGTH_SHORT);
} catch (IOException e) {
e.printStackTrace();
} catch (FormatException e) {
e.printStackTrace();
}finally {
try {
ndef.close();//關閉連接
} catch (IOException e) {
e.printStackTrace();
}
}
}
讀Ndef文本數(shù)據(jù)
//讀取Ndef標簽中數(shù)據(jù)
private void readNdef(){
if (mTag==null){
Toast.makeText(this,"不能識別的標簽類型!",Toast.LENGTH_SHORT);
finish();
return;
}
Ndef ndef=Ndef.get(mTag);//獲取ndef對象
try {
ndef.connect();//連接
NdefMessage ndefMessage=ndef.getNdefMessage();//獲取NdefMessage對象
if (ndefMessage!=null) readEdt.setText(parseTextRecord(ndefMessage.getRecords()[0]));
Toast.makeText(this,"數(shù)據(jù)讀取成功!",Toast.LENGTH_SHORT);
} catch (IOException e) {
e.printStackTrace();
} catch (FormatException e) {
e.printStackTrace();
}finally {
try {
ndef.close();//關閉鏈接
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 解析NDEF文本數(shù)據(jù),從第三個字節(jié)開始,后面的文本數(shù)據(jù)
* @param ndefRecord
* @return
*/
public static String parseTextRecord(NdefRecord ndefRecord) {
/**
* 判斷數(shù)據(jù)是否為NDEF格式
*/
//判斷TNF
if (ndefRecord.getTnf() != NdefRecord.TNF_WELL_KNOWN) {
return null;
}
//判斷可變的長度的類型
if (!Arrays.equals(ndefRecord.getType(), NdefRecord.RTD_TEXT)) {
return null;
}
try {
//獲得字節(jié)數(shù)組,然后進行分析
byte[] payload = ndefRecord.getPayload();
//下面開始NDEF文本數(shù)據(jù)第一個字節(jié),狀態(tài)字節(jié)
//判斷文本是基于UTF-8還是UTF-16的,取第一個字節(jié)"位與"上16進制的80,16進制的80也就是最高位是1,
//其他位都是0,所以進行"位與"運算后就會保留最高位
String textEncoding = ((payload[0] & 0x80) == 0) ? "UTF-8" : "UTF-16";
//3f最高兩位是0,第六位是1,所以進行"位與"運算后獲得第六位
int languageCodeLength = payload[0] & 0x3f;
//下面開始NDEF文本數(shù)據(jù)第二個字節(jié),語言編碼
//獲得語言編碼
String languageCode = new String(payload, 1, languageCodeLength, "US-ASCII");
//下面開始NDEF文本數(shù)據(jù)后面的字節(jié),解析出文本
String textRecord = new String(payload, languageCodeLength + 1,
payload.length - languageCodeLength - 1, textEncoding);
return textRecord;
} catch (Exception e) {
throw new IllegalArgumentException();
}
}
MifareClassic格式標簽讀寫
MifareClassic格式標簽數(shù)據(jù)結(jié)構(gòu)
第一扇區(qū)的第一塊一般用于制造商占用塊
0-15個扇區(qū):一個扇區(qū)對應4個塊,所以總共有64個塊,序號分別為0-63,第一個扇區(qū)對應:0-3塊,第二個扇區(qū)對應:4-7塊…
每個扇區(qū)的最后一個塊用來存放密碼或控制位,其余為數(shù)據(jù)塊,一個塊占用16個字節(jié),keyA占用6字節(jié),控制位占用4字節(jié),keyB占用6字節(jié)。
MifareClassic標簽讀寫常用api:
get():根據(jù)Tag對象來獲得MifareClassic對象;
Connect():允許對MifareClassic標簽進行IO操作;
getType():獲得MifareClassic標簽的具體類型:TYPE_CLASSIC,TYPE_PLUA,TYPE_PRO,TYPE_UNKNOWN;
getSectorCount():獲得標簽總共有的扇區(qū)數(shù)量;
getBlockCount():獲得標簽總共有的的塊數(shù)量;
getSize():獲得標簽的容量:SIZE_1K,SIZE_2K,SIZE_4K,SIZE_MINI
authenticateSectorWithKeyA(int SectorIndex,byte[] Key):驗證當前扇區(qū)的KeyA密碼,返回值為ture或false。 常用KeyA:默認出廠密碼:KEY_DEFAULT,各種用途的供貨商必須配合該技術(shù)的MAD:KEY_MIFARE_APPLICATION_DIRECTORY
被格式化成NDEF格式的密碼:KEY_NFC_FORUM
getBlockCountInSector(int):獲得當前扇區(qū)的所包含塊的數(shù)量;
sectorToBlock(int):當前扇區(qū)的第1塊的塊號;
writeBlock(int,data):將數(shù)據(jù)data寫入當前塊;注意:data必須剛好是16Byte,末尾不能用0填充,應該用空格
readBlock(int):讀取當前塊的數(shù)據(jù)。
close():禁止對標簽的IO操作,釋放資源。
寫MifareClassic格式標簽數(shù)據(jù)
//寫塊
private void writeBlock(){
if (mTag==null){
Toast.makeText(this,"無法識別的標簽!",Toast.LENGTH_SHORT);
finish();
return;
}
if (!haveMifareClissic){
Toast.makeText(this,"不支持MifareClassic",Toast.LENGTH_SHORT);
finish();
return;
}
MifareClassic mfc=MifareClassic.get(mTag);
try {
mfc.connect();//打開連接
boolean auth;
int sector=Integer.parseInt(sectorNum.getText().toString().trim());//寫入的扇區(qū)
int block=Integer.parseInt(blockNum.getText().toString().trim());//寫入的塊區(qū)
auth=mfc.authenticateSectorWithKeyA(sector,MifareClassic.KEY_DEFAULT);//keyA驗證扇區(qū)
if (auth){
mfc.writeBlock(block,"0123456789012345".getBytes());//寫入數(shù)據(jù)
Toast.makeText(this,"寫入成功!",Toast.LENGTH_SHORT);
}
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
mfc.close();//關閉連接
} catch (IOException e) {
e.printStackTrace();
}
}
}
讀MifareClassic格式標簽數(shù)據(jù)
//讀取塊
private void readBlock(){
if (mTag==null){
Toast.makeText(this,"無法識別的標簽!",Toast.LENGTH_SHORT);
finish();
return;
}
if (!haveMifareClissic){
Toast.makeText(this,"不支持MifareClassic",Toast.LENGTH_SHORT);
finish();
return;
}
MifareClassic mfc=MifareClassic.get(mTag);
try {
mfc.connect();//打開連接
boolean auth;
int sector=Integer.parseInt(sectorNum.getText().toString().trim());//寫入的扇區(qū)
int block=Integer.parseInt(blockNum.getText().toString().trim());//寫入的塊區(qū)
auth=mfc.authenticateSectorWithKeyA(sector,MifareClassic.KEY_DEFAULT);//keyA驗證扇區(qū)
if (auth){
readData.setText(bytesToHexString(mfc.readBlock(block)));
}
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
mfc.close();//關閉連接
} catch (IOException e) {
e.printStackTrace();
}
}
}
//字符序列轉(zhuǎn)換為16進制字符串
private String bytesToHexString(byte[] src) {
StringBuilder stringBuilder = new StringBuilder("0x");
if (src == null || src.length <= 0) {
return null;
}
char[] buffer = new char[2];
for (int i = 0; i < src.length; i++) {
buffer[0] = Character.forDigit((src[i] >>> 4) & 0x0F, 16);
buffer[1] = Character.forDigit(src[i] & 0x0F, 16);
System.out.println(buffer);
stringBuilder.append(buffer);
}
return stringBuilder.toString();
}
總結(jié)
以上是生活随笔為你收集整理的android nfc ndef mifareclassic,Android NFC开发-实践篇的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: linux计算目录下wc,Linux w
- 下一篇: 信号的采样与恢复matlab实验报告,实