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