物聯(lián)傳媒 旗下網(wǎng)站
登錄 注冊(cè)

什么是NFC近場(chǎng)通信

作者:老歐
來(lái)源:公眾號(hào)/老歐說(shuō)安卓
日期:2018-02-22 09:32:01
摘要:帶有NFC功能的手機(jī),在實(shí)際生活中主要有三項(xiàng)應(yīng)用:讀卡、寫卡、分享內(nèi)容(兩部手機(jī)之間傳輸數(shù)據(jù))。為了能更迅速地了解NFC技術(shù)在Android中的開(kāi)發(fā)流程,下面通過(guò)相對(duì)簡(jiǎn)單的讀卡功能,來(lái)介紹如何進(jìn)行手機(jī)App的NFC開(kāi)發(fā)。
關(guān)鍵詞:NFCRFID通信

  NFC的全稱是“Near Field Communication”,意思是近場(chǎng)通信、與鄰近的區(qū)域通信。大眾所熟知的NFC技術(shù)應(yīng)用,主要是智能手機(jī)的刷卡支付功能。別看智能手機(jī)是近十年前才出現(xiàn)的,NFC的歷史可比智能手機(jī)要悠久得多,它脫胎于上世紀(jì)的RFID無(wú)線射頻識(shí)別技術(shù)。

  所謂RFID是“Radio Frequency Identification”的縮寫,它通過(guò)無(wú)線電信號(hào)便可識(shí)別特定目標(biāo)并讀寫數(shù)據(jù),而無(wú)需自身與該目標(biāo)之間建立任何機(jī)械或者光學(xué)接觸。像日常生活中的門禁卡、公交卡,乃至二代身份證,都是采用了RFID技術(shù)的卡片。若想讀寫這些RFID卡片,則需相應(yīng)的讀卡器,只要用戶把卡片靠近,讀卡器就會(huì)產(chǎn)生感應(yīng)動(dòng)作。

  既然RFID已經(jīng)廣泛使用,那么何苦又要另外制定NFC標(biāo)準(zhǔn)呢?其實(shí)正是因?yàn)镽FID用的地方太多了,導(dǎo)致隨意性較大,反而不便于更好地管控。所以業(yè)界重新定義了NFC規(guī)范,試圖在兩個(gè)方面彌補(bǔ)RFID的固有缺憾:

  1、RFID的信號(hào)傳播距離較遠(yuǎn),致使位于遠(yuǎn)處的設(shè)備也可能獲取卡片信息,這對(duì)安全性較高的場(chǎng)合是不可接受的。而NFC的有效工作距離在十厘米之內(nèi),即可避免卡片信息被竊取的風(fēng)險(xiǎn)。

  2、RFID的讀寫操作是單向的,也就是說(shuō),只有讀卡器能讀寫卡片,卡片不能拿讀卡器怎么樣?,F(xiàn)在NFC不再沿用“讀卡器——卡片”的模式,取而代之的是只有NFC設(shè)備的概念,兩個(gè)NFC設(shè)備允許互相讀寫,既可以由設(shè)備A讀寫設(shè)備B,也可以由設(shè)備B讀寫設(shè)備A。

  改進(jìn)之后的NFC技術(shù)既提高了安全性,又拓寬了應(yīng)用場(chǎng)合,同時(shí)還兼容現(xiàn)有的大部分RFID卡片,因此在智能手機(jī)上運(yùn)用NFC而非RFID也就不足為怪了。

  帶有NFC功能的手機(jī),在實(shí)際生活中主要有三項(xiàng)應(yīng)用:讀卡、寫卡、分享內(nèi)容(兩部手機(jī)之間傳輸數(shù)據(jù))。為了能更迅速地了解NFC技術(shù)在Android中的開(kāi)發(fā)流程,下面通過(guò)相對(duì)簡(jiǎn)單的讀卡功能,來(lái)介紹如何進(jìn)行手機(jī)App的NFC開(kāi)發(fā)。

  首先App工程要在AndroidManifest.xml中聲明NFC的操作權(quán)限,下面是配置聲明的例子:

  <!--NFC-->

  <uses-permission android:name="android.permission.NFC"/>

  <uses-feature android:name="android.hardware.nfc"android:required="true"/>

  其次還要對(duì)活動(dòng)頁(yè)面聲明NFC過(guò)濾器,目前Android支持NDEF_DISCOVERED、TAG_DISCOVERED、TECH_DISCOVERED這三種過(guò)濾器,最好把它們都加入到過(guò)濾器列表中,示例如下:

  <activity android:name=".NfcActivity">

  <intent-filter>

  <action android:name="android.nfc.action.NDEF_DISCOVERED"/>

  </intent-filter>

  <intent-filter>

  <action android:name="android.nfc.action.TAG_DISCOVERED"/>

  <category android:name="android.intent.category.DEFAULT"/>

  </intent-filter>

  <intent-filter>

  <action android:name="android.nfc.action.TECH_DISCOVERED"/>

  </intent-filter>

  <meta-data

  android:name="android.nfc.action.TECH_DISCOVERED"

  android:resource=" xml/nfc_tech_filter"/>

  </activity>

  其中TECH_DISCOVERED類型另外指定了過(guò)濾器的來(lái)源是 xml/nfc_tech_filter,該文件的實(shí)際路徑為xml/nfc_tech_filter.xml,文件內(nèi)容如下所示:

  <resources>

  <!--可以處理所有Android支持的NFC類型-->

  <tech-list>

  <tech>android.nfc.tech.NfcA</tech>

  <tech>android.nfc.tech.NfcB</tech>

  <tech>android.nfc.tech.NfcF</tech>

  <tech>android.nfc.tech.NfcV</tech>

  <tech>android.nfc.tech.IsoDep</tech>

  <tech>android.nfc.tech.Ndef</tech>

  <tech>android.nfc.tech.NdefFormatable</tech>

  <tech>android.nfc.tech.MifareClassic</tech>

  <tech>android.nfc.tech.MifareUltralight</tech>

  </tech-list>

  </resources>

  上面的過(guò)濾器列表乍看過(guò)去真是令人大吃一驚,這都是些什么東東,它們之間有哪些區(qū)別呢?倘若認(rèn)真對(duì)這幾個(gè)專業(yè)術(shù)語(yǔ)追根溯源,勢(shì)必要一番長(zhǎng)篇大論才能理清其中的歷史脈絡(luò),因此不妨將事情簡(jiǎn)單化,這些NFC類型只不過(guò)是一個(gè)大家族內(nèi)部的兄弟姐妹罷了。譬如說(shuō)中國(guó)近代史上顯赫的宋氏三姐妹,原是同一對(duì)父母,然后分別嫁給三個(gè)人罷了。NFC類型雖多,常見(jiàn)的NfcA、NfcB、IsoDep三個(gè)系出ISO14443標(biāo)準(zhǔn)(即RFID卡標(biāo)準(zhǔn)),它們仨各自用于生活中的幾種場(chǎng)合,說(shuō)明如下:

  1、NfcA遵循ISO14443-3A標(biāo)準(zhǔn),常用于門禁卡;

  2、NfcB遵循ISO14443-3B標(biāo)準(zhǔn),常用于二代身份證;

  3、IsoDep遵循ISO14443-4標(biāo)準(zhǔn),常用于公交卡;

  好不容易把AndroidManifest.xml的相關(guān)配置弄完,接著便是代碼方面的處理邏輯了。NFC編碼主要有三個(gè)步驟:初始化適配器、啟用感應(yīng)/禁用感應(yīng)、接收到感應(yīng)消息并對(duì)消息解碼,下面分別進(jìn)行介紹:

  一、初始化NFC適配器

  這里的初始化動(dòng)作又可分解為三部分:

  1、調(diào)用NfcAdapter類的getDefaultAdapter方法,獲取系統(tǒng)當(dāng)前默認(rèn)的NFC適配器。

  2、聲明一個(gè)延遲意圖,告訴系統(tǒng)一旦接收到NFC感應(yīng),則應(yīng)當(dāng)啟動(dòng)哪個(gè)頁(yè)面進(jìn)行處理。

  3、定義一個(gè)NFC消息的過(guò)濾器,這個(gè)過(guò)濾器是AndroidManifest.xml所配置過(guò)濾器的子集。因?yàn)榻酉聛?lái)要讀取的卡片兼容RFID標(biāo)準(zhǔn)(ISO14443家族),所以過(guò)濾器的動(dòng)作名稱為NfcAdapter.ACTION_TECH_DISCOVERED,并且設(shè)置該動(dòng)作包含了兩項(xiàng)卡片標(biāo)準(zhǔn),分別是NfcA(用于門禁卡)和IsoDep(用于公交卡)。

  詳細(xì)的NFC初始化代碼示例如下:

  private void initNfc(){

  //獲取默認(rèn)的NFC適配器

  nfcAdapter=NfcAdapter.getDefaultAdapter(this);

  if(nfcAdapter==null){

  tv_nfc_result.setText("當(dāng)前手機(jī)不支持NFC");

  return;

  }else if(!nfcAdapter.isEnabled()){

  tv_nfc_result.setText("請(qǐng)先在系統(tǒng)設(shè)置中啟用NFC功能");

  return;

  }

  //探測(cè)到NFC卡片后,必須以FLAG_ACTIVITY_SINGLE_TOP方式啟動(dòng)Activity,

  //或者在AndroidManifest.xml中設(shè)置launchMode屬性為singleTop或者singleTask,

  //保證無(wú)論NFC標(biāo)簽靠近手機(jī)多少次,Activity實(shí)例都只有一個(gè)。

  Intent intent=new Intent(this,NfcActivity.class).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);

  //聲明一個(gè)NFC卡片探測(cè)事件的相應(yīng)動(dòng)作

  mPendingIntent=PendingIntent.getActivity(this,0,intent,PendingIntent.FLAG_UPDATE_CURRENT);

  try{

  //定義一個(gè)過(guò)濾器(檢測(cè)到NFC卡片)

  mFilters=new IntentFilter[]{new IntentFilter(NfcAdapter.ACTION_TECH_DISCOVERED,"*/*")};

  }catch(Exception e){

  e.printStackTrace();

  }

  //讀標(biāo)簽之前先確定標(biāo)簽類型

  mTechLists=new String[][]{new String[]{NfcA.class.getName()},{IsoDep.class.getName()}};

  }

  二、啟用NFC感應(yīng)/禁用NFC感應(yīng)

  為了讓測(cè)試App能夠接收NFC的感應(yīng)動(dòng)作,需要重載Activity的onResume函數(shù),在該函數(shù)中調(diào)用NFC適配器的enableForegroundDispatch方法,指定啟用NFC功能時(shí)的響應(yīng)動(dòng)作以及過(guò)濾條件。另外也需重載onPause函數(shù),在該函數(shù)中調(diào)用NFC適配器的disableForegroundDispatch方法,表示當(dāng)前頁(yè)面在暫停狀態(tài)之時(shí)不再接收NFC感應(yīng)消息。具體的NFC啟用和禁用代碼如下所示:

   Override

  protected void onResume(){

  super.onResume();

  if(nfcAdapter!=null&&nfcAdapter.isEnabled()){

  //為本App啟用NFC感應(yīng)

  nfcAdapter.enableForegroundDispatch(this,mPendingIntent,mFilters,mTechLists);

  }

  }

   Override

  public void onPause(){

  super.onPause();

  if(nfcAdapter!=null&&nfcAdapter.isEnabled()){

  //禁用本App的NFC感應(yīng)

  nfcAdapter.disableForegroundDispatch(this);

  }

  }

  三、接收到感應(yīng)消息并對(duì)消息解碼

  通過(guò)前面的第二步啟用NFC感應(yīng)之后,一旦App接收到感應(yīng)消息,就會(huì)回調(diào)Activity的onNewIntent函數(shù),因此開(kāi)發(fā)者可以重寫該函數(shù)來(lái)處理NFC的消息內(nèi)容。以NFC技術(shù)常見(jiàn)的小區(qū)門禁卡為例,門禁卡采取的子標(biāo)準(zhǔn)為NfcA,對(duì)應(yīng)的數(shù)據(jù)格式則為MifareClassic。于是利用MifareClassic類的相關(guān)方法即可獲取卡片數(shù)據(jù),下面是MifareClassic類的方法說(shuō)明:

  get:從Tag對(duì)象中獲取卡片對(duì)象的信息。該方法為靜態(tài)方法。

  connect:連接卡片數(shù)據(jù)。

  close:釋放卡片數(shù)據(jù)。

  getType:獲取卡片的類型。TYPE_CLASSIC表示傳統(tǒng)類型,TYPE_PLUS表示增強(qiáng)類型,TYPE_PRO表示專業(yè)類型。

  getSectorCount:獲取卡片的扇區(qū)數(shù)量。

  getBlockCount:獲取卡片的分塊個(gè)數(shù)。

  getSize:獲取卡片的存儲(chǔ)空間大小,單位字節(jié)。

  使用MifareClassic工具查詢卡片數(shù)據(jù)的流程很常規(guī),先調(diào)用connect方法建立連接,然后調(diào)用各個(gè)get方法獲取詳細(xì)信息,最后調(diào)用close方法關(guān)閉連接。具體的門禁卡讀取代碼示例如下:

   Override

  protected void onNewIntent(Intent intent){

  super.onNewIntent(intent);

  String card_info="";

  String action=intent.getAction();//獲取到本次啟動(dòng)的action

  if(action.equals(NfcAdapter.ACTION_NDEF_DISCOVERED)//NDEF類型

  ||action.equals(NfcAdapter.ACTION_TECH_DISCOVERED)//其他類型

  ||action.equals(NfcAdapter.ACTION_TAG_DISCOVERED)){//未知類型

  //從intent中讀取NFC卡片內(nèi)容

  Tag tag=intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);

  //獲取NFC卡片的序列號(hào)

  byte[]ids=tag.getId();

  card_info=String.format("卡片的序列號(hào)為:%s",ByteArrayChange.ByteArrayToHexString(ids));

  if(rb_guard_card.isChecked()){

  String result=readGuardCard(tag);

  card_info=String.format("%s\n詳細(xì)信息如下:\n%s",card_info,result);

  tv_nfc_result.setText(card_info);

  }

  }

  }

  //讀取小區(qū)門禁卡信息

  public String readGuardCard(Tag tag){

  MifareClassic classic=MifareClassic.get(tag);

  String info;

  try{

  classic.connect();//連接卡片數(shù)據(jù)

  int type=classic.getType();//獲取TAG的類型

  String typeDesc;

  if(type==MifareClassic.TYPE_CLASSIC){

  typeDesc="傳統(tǒng)類型";

  }else if(type==MifareClassic.TYPE_PLUS){

  typeDesc="增強(qiáng)類型";

  }else if(type==MifareClassic.TYPE_PRO){

  typeDesc="專業(yè)類型";

  }else{

  typeDesc="未知類型";

  }

  info=String.format("\t卡片類型:%s\n\t扇區(qū)數(shù)量:%d\n\t分塊個(gè)數(shù):%d\n\t存儲(chǔ)空間:%d字節(jié)",

  typeDesc,classic.getSectorCount(),classic.getBlockCount(),classic.getSize());

  }catch(Exception e){

  e.printStackTrace();

  info=e.getMessage();

  }finally{//無(wú)論是否發(fā)生異常,都要釋放資源

  try{

  classic.close();//釋放卡片數(shù)據(jù)

  }catch(Exception e){

  e.printStackTrace();

  info=e.getMessage();

  }

  }

  return info;

  }

  編碼完畢,找一臺(tái)支持NFC的手機(jī)安裝測(cè)試App,啟動(dòng)應(yīng)用前注意開(kāi)啟手機(jī)的NFC功能。然后進(jìn)入App的測(cè)試頁(yè)面,拿一張門禁卡靠近手機(jī)背面(門禁卡不一定是卡片,也可能是鑰匙扣模樣),稍等片刻便會(huì)讀取并顯示門禁卡的基本信息,卡片信息截圖如下所示: