日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 >

android nfc ndef mifareclassic,Android NFC开发-实践篇

發(fā)布時間:2025/3/12 69 豆豆
生活随笔 收集整理的這篇文章主要介紹了 android nfc ndef mifareclassic,Android NFC开发-实践篇 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

Android NFC開發(fā)-實踐篇

https://blog..net/_GYG/article/details/72899417

在Android NFC開發(fā)-理論篇中,我們了解了在Android中開發(fā)NFC的一些理論知識,這篇我們繼續(xù)應(yīng)用我們上一篇學(xué)到的知識,實現(xiàn)對NDEF格式標簽和MifareClassic格式標簽的讀寫操作。

基本操作

配置AndroidMenifest.xml:

獲取設(shè)備默認的NfcAdapter對象,判斷該設(shè)備是否支持NFC功能,若支持,判斷此功能是否打開,并且創(chuàng)建一個PendingIntent對象,用于當NFC標簽被檢測到時,啟動我們處理NFC標簽的Activity

@Override

protected void onStart() {

super.onStart();

mNfcAdapter= NfcAdapter.getDefaultAdapter(this);//設(shè)備的NfcAdapter對象

if(mNfcAdapter==null){//判斷設(shè)備是否支持NFC功能

Toast.makeText(this,"設(shè)備不支持NFC功能!",Toast.LENGTH_SHORT);

finish();

return;

}

if (!mNfcAdapter.isEnabled()){//判斷設(shè)備NFC功能是否打開

Toast.makeText(this,"請到系統(tǒng)設(shè)置中打開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失去焦點時,關(guān)閉前臺發(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);//關(guān)閉前臺發(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,對于需要兼容更小版本的應(yīng)用來說就需要我們自己來實現(xiàn)這個方法。

不管什么格式的數(shù)據(jù)本質(zhì)上都是由一些字節(jié)組成的。對于NDEF文本格式來說,這些數(shù)據(jù)的第1個字節(jié)描述了數(shù)據(jù)的狀態(tài),然后若干個字節(jié)描述文本的語言編碼,最后剩余字節(jié)表示文本數(shù)據(jù)。這些數(shù)據(jù)格式由NFC Forum的相關(guān)規(guī)范定義,可以通過?http://members.nfc-forum.org/specs/spec_dashboard?下載相關(guān)的規(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

必須設(shè)為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);

//設(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è)置第一個狀態(tài)字節(jié),先將狀態(tài)碼轉(zhuǎn)換成字節(jié)

data[0] = (byte) status;

//設(shè)置語言編碼,使用數(shù)組拷貝方法,從0開始拷貝到data中,拷貝到data的1到langBytes.length的位置

System.arraycopy(langBytes, 0, data, 1, langBytes.length);

//設(shè)置文本字節(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();//關(guān)閉連接

} 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();//關(guān)閉鏈接

} 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ū)對應(yīng)4個塊,所以總共有64個塊,序號分別為0-63,第一個扇區(qū)對應(yīng):0-3塊,第二個扇區(qū)對應(yīng):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填充,應(yīng)該用空格

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();//關(guān)閉連接

} 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();//關(guān)閉連接

} 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)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。