在上一篇中的最後寫到了如何過濾和接收一個nfc intent。那麼這裏要寫如何解析已經收到的nfc intent。android
一:解析NDEFweb
一直在說ndef格式,這究竟是個啥格式?據文檔說這是google參考nfc論壇而提出和支持的格式。也就是說nfc的格式和標準確定有不少,google只不過比較支持其中的這一種NDEF。而若是以後apple也搞nfc的話,我懷疑他確定就不會去支持google已經支持的ndef了。我淺薄的如此認爲,之後再看看吧呵呵json
ndef格式,就是數據被包裹在一個NdefMessage裏,而同時一個NdefMessage裏面又能夠包含多個NdefRecord.固然你也應該還記得,nfc的tag裏面是能夠不包含Ndef數據的,他也能夠包含android.nfc.tech所定義的多種標籤。Google推薦開發人員使用ndef格式的數據來處理android相關的nfc格式數據。瀏覽器
當一個android設備檢測到nfc標籤包含ndef格式的數據的時候,他就會嘗試的去解析數據中的MIME type或者是URI。爲了作這個事情,他須要解析NdefMessage中的第一個NdefRecord。第一個NdefRecord中的結構通常以下:網絡
3-bit TNF (Type Name Format)app
如何解釋variable length type的數據。並依據不一樣的解析,來肯定發送什麼樣的intent。若是是這裏定義的是TNF_WELL_KNOWN則還要參考variable length type來進一步肯定。若是是網絡地址URL則直接去調用瀏覽器程序了。google
Variable length typespa
進一步定義record的類型。具體的請參看android文檔。不過話在這裏多說一句,TNF和TYPE這兩個參數很是的重要,直接決定了你NDEF格式的命中程度。定義的越規範,越清楚,同時在androidmanifest文件中寫的越正確,就越好,不然系統就會被你發TECH_ACTION翻譯
好比:構造的時候寫orm
new NdefRecord(
NdefRecord.TNF_WELL_KNOWN ,
NdefRecord.RTD_TEXT,
new byte[0],
data.getBytes(Charset.forName("UTF-8")));
androidmanifest文件中寫
<intent-filter>
<action android:name="android.nfc.action.NDEF_DISCOVERED"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:mimeType="text/plain"/>
</intent-filter>
這一對就能夠正確的過濾一個text/plain格式的ndef
Variable length ID
不常用.....是用來惟一標示一個record的。
Variable length payload
actual這個纔是真正的數據區域。你的讀寫操做通常應該主要是對這個區域的操做。一個NdefMessage能夠有多個NdefRecord因此你不要假設全部的數據都僅僅存儲在第一個NdefRecord中。
已經知道了Ndef的格式了,那麼解析帶有Ndef數據的Intent也就簡單不少了。底下這個解析的方法,一般放在onResume或者onNewIntent中。
private boolean readFromTag(Intent in){
Parcelable[] rawArray = in.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);
//we just have noly one NdefMessage,若是你不止一個的話,那麼你要遍歷了。
NdefMessage mNdefMsg = (NdefMessage)rawArray[0];
//we just have only one NdefRecord,若是你不止一個record,那麼你也要遍歷出來全部的record
NdefRecord mNdefRecord = mNdefMsg.getRecords()[0];
try {
if(mNdefRecord != null){
readResult = new String(mNdefRecord.getPayload(),"UTF-8");
readJson = new JSONObject(readResult);//我舉的例子中用的是json格式,因此這裏要把payload中的數據封裝成json格式的。
return true;
}
} catch (JSONException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
};
return false;
}
二:解析其餘tech標籤
如今你已經會解析ndef標籤了,可是你也應該知道,大多數nfc標籤其實並非ndef格式的。好比你的公交卡,各個城市的公交卡會支持什麼格式,並不必定。至少我理解是這樣的。因此你還要會解析各類tech格式的nfc數據。
若是你還記得咱們再tech-list中定義了多少種格式,你就應該知道咱們若是要所有解析的話,應該要解析多少種不一樣的nfc格式了。底下這段代碼能夠顯示出你所掃描的nfc卡到底支持哪幾種格式。
Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
String[] temp = tag.getTechList();
for(String s :temp){
Log.i("xxx","s = "+s);
}
這是一段最經常使用的(我我的理解啊)的nfc解析代碼,主要是解析MifareClassic格式。貌似大多數nfc標籤都會支持這種格式。先普及下關於他的簡單常識:不知道各個格式的數據是怎麼構成的,你壓根就無法解析。
通常來講,給予MifareClassic的射頻卡,通常內存大小有3種:
1K: 16個分區(sector),每一個分區4個塊(block),每一個塊(block) 16個byte數據
2K: 32個分區,每一個分區4個塊(block),每一個塊(block) 16個byte數據
4K:64個分區,每一個分區4個塊(block),每一個塊(block) 16個byte數據
對於全部基於MifareClassic的卡來講,每一個區最後一個塊(block)叫Trailer,16個byte,主要來存放讀寫該區的key,能夠有A,B兩個KEY,每一個key長6byte,默認的key通常是FF 或 0,最後一個塊的內存結構以下:
Block 0 Data 16bytes
Block 1 Data 16 bytes
Block 2 Data 16 bytes
Block 3 Trailer 16 bytes
Trailer:
Key A: 6 bytes
Access Conditions: 4 bytes
Key B: 6 bytes
因此在寫卡的內存的時候,通常不能寫每一個sector的最後一個block,除非你有要修改KEY和訪問權限的需求。若是KEY A 被你不當心修改掉了,而你不知道修改爲什麼,那與之對應的那個sector你就沒有辦法訪問了。由於在MifareClassic中,若是你要讀取數據,那麼必需要有這個數據地址所在的sector的權限,這個權限就是這個sector的trailer的KEY A或KEY B。
這就是解析MifareClassic的代碼了
private void readTechPayloadMC(Tag tag){
MifareClassic mc = MifareClassic.get(tag);//經過intent拿到EXTRA_TAG並轉化成MirareClassic格式。
int bCount = 0 ;
int bIndex = 0 ;
try {
mc.connect();
int sectorCount = mc.getSectorCount();//得到sector總數
Log.i("liyufei3","sectorCount = "+sectorCount);
for(int i =0;i<sectorCount;i++){
//嘗試去得到每一個sector的認證,只有認證經過才能訪問
auth = mc.authenticateSectorWithKeyA(i, MifareClassic.KEY_DEFAULT);
if(auth){
//這句其實不是必須的,由於每一個sector中原本就只有4個block
bCount = mc.getBlockCountInSector(i);
//all blocks are consecutively numbered ,0-64,so we can not use number of cycles to be index.
//we just can get the first block in iTH sector
//咱們能夠獲得每個sector中的第一個block的編號
bIndex = mc.sectorToBlock(i);
for(int j =0;j<bCount;j++){//循環四次拿出一個sector中全部的block
//每次循環bIndex會去++,而後能夠得出每個block的數據。這些數據是字節碼,因此你還有一個翻譯的工做要作。
byte[] data = mc.readBlock(bIndex);
String s = readByteArray(data);
bIndex++;
}
}else{
Log.i("xxx"," auth = false !!! in sectorCount = "+i);
}
}
} catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } }