NFC的全稱是“Near Field Communication”,意思是近場通信、與鄰近的區域通信。大衆所熟知的NFC技術應用,主要是智能手機的刷卡支付功能。别看智能手機是近十年前才出現的,NFC的曆史可比智能手機要悠久得多,它脫胎于上世紀的RFID無線射頻識别技術。
所謂RFID是“Radio Frequency Identification”的縮寫,它通過無線電信号便可識别特定目标并讀寫數據,而無需自身與該目标之間建立任何機械或者光學接觸。像日常生活中的門禁卡、公交卡,乃至二代身份證,都是采用了RFID技術的卡片。若想讀寫這些RFID卡片,則需相應的讀卡器,隻要用戶把卡片靠近,讀卡器就會産生感應動作。
既然RFID已經廣泛使用,那麼何苦又要另外制定NFC标準呢?其實正是因為RFID用的地方太多了,導緻随意性較大,反而不便于更好地管控。所以業界重新定義了NFC規範,試圖在兩個方面彌補RFID的固有缺憾:
1、RFID的信号傳播距離較遠,緻使位于遠處的設備也可能獲取卡片信息,這對安全性較高的場合是不可接受的。而NFC的有效工作距離在十厘米之内,即可避免卡片信息被竊取的風險。
2、RFID的讀寫操作是單向的,也就是說,隻有讀卡器能讀寫卡片,卡片不能拿讀卡器怎麼樣。現在NFC不再沿用“讀卡器——卡片”的模式,取而代之的是隻有NFC設備的概念,兩個NFC設備允許互相讀寫,既可以由設備A讀寫設備B,也可以由設備B讀寫設備A。
改進之後的NFC技術既提高了安全性,又拓寬了應用場合,同時還兼容現有的大部分RFID卡片,因此在智能手機上運用NFC而非RFID也就不足為怪了。
帶有NFC功能的手機,在實際生活中主要有三項應用:讀卡、寫卡、分享内容(兩部手機之間傳輸數據)。為了能更迅速地了解NFC技術在Android中的開發流程,下面通過相對簡單的讀卡功能,來介紹如何進行手機App的NFC開發。
首先App工程要在AndroidManifest.xml中聲明NFC的操作權限,下面是配置聲明的例子:
其次還要對活動頁面聲明NFC過濾器,目前Android支持NDEF_DISCOVERED、TAG_DISCOVERED、TECH_DISCOVERED這三種過濾器,最好把它們都加入到過濾器列表中,示例如下:
android:name="android.nfc.action.TECH_DISCOVERED"
android:resource=" xml/nfc_tech_filter"/>
其中TECH_DISCOVERED類型另外指定了過濾器的來源是 xml/nfc_tech_filter,該文件的實際路徑為xml/nfc_tech_filter.xml,文件内容如下所示:
上面的過濾器列表乍看過去真是令人大吃一驚,這都是些什麼東東,它們之間有哪些區别呢?倘若認真對這幾個專業術語追根溯源,勢必要一番長篇大論才能理清其中的曆史脈絡,因此不妨将事情簡單化,這些NFC類型隻不過是一個大家族内部的兄弟姐妹罷了。譬如說中國近代史上顯赫的宋氏三姐妹,原是同一對父母,然後分别嫁給三個人罷了。NFC類型雖多,常見的NfcA、NfcB、IsoDep三個系出ISO14443标準(即RFID卡标準),它們仨各自用于生活中的幾種場合,說明如下:
1、NfcA遵循ISO14443-3A标準,常用于門禁卡;
2、NfcB遵循ISO14443-3B标準,常用于二代身份證;
3、IsoDep遵循ISO14443-4标準,常用于公交卡;
好不容易把AndroidManifest.xml的相關配置弄完,接着便是代碼方面的處理邏輯了。NFC編碼主要有三個步驟:初始化适配器、啟用感應/禁用感應、接收到感應消息并對消息解碼,下面分别進行介紹:
一、初始化NFC适配器
這裡的初始化動作又可分解為三部分:
1、調用NfcAdapter類的getDefaultAdapter方法,獲取系統當前默認的NFC适配器。
2、聲明一個延遲意圖,告訴系統一旦接收到NFC感應,則應當啟動哪個頁面進行處理。
3、定義一個NFC消息的過濾器,這個過濾器是AndroidManifest.xml所配置過濾器的子集。因為接下來要讀取的卡片兼容RFID标準(ISO14443家族),所以過濾器的動作名稱為NfcAdapter.ACTION_TECH_DISCOVERED,并且設置該動作包含了兩項卡片标準,分别是NfcA(用于門禁卡)和IsoDep(用于公交卡)。
詳細的NFC初始化代碼示例如下:
private void initNfc{
//獲取默認的NFC适配器
nfcAdapter=NfcAdapter.getDefaultAdapter(this);
if(nfcAdapter==null){
tv_nfc_result.setText("當前手機不支持NFC");
return;
}else if(!nfcAdapter.isEnabled){
tv_nfc_result.setText("請先在系統設置中啟用NFC功能");
return;
}
//探測到NFC卡片後,必須以FLAG_ACTIVITY_SINGLE_TOP方式啟動Activity,
//或者在AndroidManifest.xml中設置launchMode屬性為singleTop或者singleTask,
//保證無論NFC标簽靠近手機多少次,Activity實例都隻有一個。
intent intent=new Intent(this,NfcActivity.class).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
//聲明一個NFC卡片探測事件的相應動作
mPendingIntent=PendingIntent.getActivity(this,0,intent,PendingIntent.FLAG_UPDATE_CURRENT);
try{
//定義一個過濾器(檢測到NFC卡片)
mFilters=new IntentFilter{new IntentFilter(NfcAdapter.ACTION_TECH_DISCOVERED,"*/*")};
}catch(Exception e){
e.printStackTrace;
}
//讀标簽之前先确定标簽類型
mTechLists=new String{new String{NfcA.class.getName},{IsoDep.class.getName}};
}
二、啟用NFC感應/禁用NFC感應
為了讓測試App能夠接收NFC的感應動作,需要重載Activity的onResume函數,在該函數中調用NFC适配器的enableForegroundDispatch方法,指定啟用NFC功能時的響應動作以及過濾條件。另外也需重載onPause函數,在該函數中調用NFC适配器的disableForegroundDispatch方法,表示當前頁面在暫停狀态之時不再接收NFC感應消息。具體的NFC啟用和禁用代碼如下所示:
Override
protected void onResume{
super.onResume;
if(nfcAdapter!=null&&nfcAdapter.isEnabled){
//為本App啟用NFC感應
nfcAdapter.enableForegroundDispatch(this,mPendingIntent,mFilters,mTechLists);
}
}
Override
public void onPause{
super.onPause;
if(nfcAdapter!=null&&nfcAdapter.isEnabled){
//禁用本App的NFC感應
nfcAdapter.disableForegroundDispatch(this);
}
}
三、接收到感應消息并對消息解碼
通過前面的第二步啟用NFC感應之後,一旦App接收到感應消息,就會回調Activity的onNewIntent函數,因此開發者可以重寫該函數來處理NFC的消息内容。以NFC技術常見的小區門禁卡為例,門禁卡采取的子标準為NfcA,對應的數據格式則為MifareClassic。于是利用MifareClassic類的相關方法即可獲取卡片數據,下面是MifareClassic類的方法說明:
get:從Tag對象中獲取卡片對象的信息。該方法為靜态方法。
connect:連接卡片數據。
close:釋放卡片數據。
getType:獲取卡片的類型。TYPE_CLASSIC表示傳統類型,TYPE_PLUS表示增強類型,TYPE_PRO表示專業類型。
getSectorCount:獲取卡片的扇區數量。
getBlockCount:獲取卡片的分塊個數。
getSize:獲取卡片的存儲空間大小,單位字節。
使用MifareClassic工具查詢卡片數據的流程很常規,先調用connect方法建立連接,然後調用各個get方法獲取詳細信息,最後調用close方法關閉連接。具體的門禁卡讀取代碼示例如下:
Override
protected void onNewIntent(Intent intent){
super.onNewIntent(intent);
String card_info="";
String action=intent.getAction;//獲取到本次啟動的action
if(action.equals(NfcAdapter.ACTION_NDEF_DISCOVERED)//NDEF類型
||action.equals(NfcAdapter.ACTION_TECH_DISCOVERED)//其他類型
||action.equals(NfcAdapter.ACTION_TAG_DISCOVERED)){//未知類型
//從intent中讀取NFC卡片内容
Tag tag=intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
//獲取NFC卡片的序列号
byteids=tag.getId;
card_info=String.format("卡片的序列号為:%s",ByteArrayChange.ByteArrayToHexString(ids));
if(rb_guard_card.isChecked){
String result=readGuardCard(tag);
card_info=String.format("%s\n詳細信息如下:\n%s",card_info,result);
tv_nfc_result.setText(card_info);
}
}
}
//讀取小區門禁卡信息
public String readGuardCard(Tag tag){
MifareClassic classic=MifareClassic.get(tag);
String info;
try{
classic.connect;//連接卡片數據
int type=classic.getType;//獲取TAG的類型
String typeDesc;
if(type==MifareClassic.TYPE_CLASSIC){
typeDesc="傳統類型";
}else if(type==MifareClassic.TYPE_PLUS){
typeDesc="增強類型";
}else if(type==MifareClassic.TYPE_PRO){
typeDesc="專業類型";
}else{
typeDesc="未知類型";
}
info=String.format("\t卡片類型:%s\n\t扇區數量:%d\n\t分塊個數:%d\n\t存儲空間:%d字節",
typeDesc,classic.getSectorCount,classic.getBlockCount,classic.getSize);
}catch(Exception e){
e.printStackTrace;
info=e.getMessage;
}finally{//無論是否發生異常,都要釋放資源
try{
classic.close;//釋放卡片數據
}catch(Exception e){
e.printStackTrace;
info=e.getMessage;
}
}
return info;
}
編碼完畢,找一台支持NFC的手機安裝測試App,啟動應用前注意開啟手機的NFC功能。然後進入App的測試頁面,拿一張門禁卡靠近手機背面(門禁卡不一定是卡片,也可能是鑰匙扣模樣),稍等片刻便會讀取并顯示門禁卡的基本信息,卡片信息截圖如下所示:
,
更多精彩资讯请关注tft每日頭條,我们将持续为您更新最新资讯!