Android NFC 開發教程

转载:http://www.imobilebbs.com/wordpress/archives/2809

Near  Field Communication (NFC) 為一短距離無線通信技術,通常有效通訊距離為4厘米以內。NFC工作頻率為13.56 兆赫茲,通信速率為106 kbit/秒到 848kbit/秒。

NFC通信總是由一個發起者(initiator)和一個接受者(target)組成。通常initiator 主動發送電磁場(RF)可以為被動式接受者(passive target)提供電源。其工作的基本原理和收音機類似。正是由於被動式接受者可以通過發起者提供電源,因此target 可以有非常簡單的形式,比如標籤,卡,sticker 的形式.

NFC 也支持點到點的通信(peer to peer) 此時參與通信的雙方都有電源支持。

和其它無線通信方式如Bluetooth相比,NFC 支持的通信帶寬和距離要小的多,但是它成本低,如價格標籤可能只有幾分錢,也不需要配對,搜尋設備等,通信雙方可以在靠近的瞬間完成交互。

在Android NFC 應用中,Android手機通常是作為通信中的發起者,也就是作為NFC 的讀寫器。Android手機也可以模擬作為NFC通信的接受者且從Android 2.3.3起也支持P2P通信。

Android對NFC的支持主要在 android.nfc 和android.nfc.tech 兩個包中。

android.nfc 包中主要類如下:

  • NfcManager 可以用來管理Android設備中指出的所有NFC Adapter,但由於大部分Android設備只支持一個NFC Adapter,可以直接使用getDefaultAapater 來獲取系統支持的Adapter。
  • NfcAdapter 為一NFC Adapter 對象,可以用來定義一個Intent使系統在檢測到NFC Tag時通知你定義的Activity,並提供用來註冊forground tag 消息發送的方法等。
  • NdefMessage 和NdefRecord NDEF 為NFC forum 定義的數據格式。
  • Tag 代表一個被動式Tag對象,可以代表一個標籤,卡片,鑰匙扣等。當Android設備檢測到一個Tag時,會創建一個Tag對象,將其放在Intent對象,然後發送到相應的Activity。

android.nfc.tech 中則定義了可以對Tag進行的讀寫操作的類,這些類按照其使用的技術類型可以分成不同的類如:NfcA, NfcB, NfcF,以及MifareClassic 等。

常見的Tag為Mifare ,後面的例子將以這種Tag 為例介紹NFC讀寫方法。


本例參考ApiDemos中NFC的ForegoundDispatch來介紹編寫Android NFC 的基本步驟,因為手邊只有MifareClassic類型的Tag ,需要對ForegoundDispatch的代碼做些修改來檢測MifareClassic 的類型的NFC Tag,讀寫其他類型的NFC Tag的基本步驟是一致的。

1.  在Android manifest 文件中申明和NFC相關的許可權和功能選項:

許可權申明:

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

最低版本要求,NFC是指Android2.3 (Level 10) 才開始支持的,因此最低版本要求必須指定為10.

<uses-sdk android:minSdkVersion=”10″/>

如果需要在Android Market上發布,需要指定手機支持NFC 功能。

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

為Activity申明它支持處理NFC Tag

比如我們的示例Activity 在Manifest 的申明如下:

<activity android:name=”.NFCDemoActivity”
android:label=”@string/app_name”
android:launchMode=”singleTop”>
<intent-filter>
<action android:name=”android.intent.action.MAIN” />
<category android:name=”android.intent.category.LAUNCHER” />
</intent-filter>
<intent-filter>
<action android:name=”android.nfc.action.NDEF_DISCOVERED”/>
<data android:mimeType=”text/plain” />
</intent-filter>
<intent-filter>
<action
android:name=”android.nfc.action.TAG_DISCOVERED”
>
</action>
<category
android:name=”android.intent.category.DEFAULT”
>
</category>
</intent-filter>
<!– Add a technology 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/filter_nfc”
/>

</activity>

三種Activity NDEF_DISCOVERED ,TECH_DISCOVERED,TAG_DISCOVERED 指明的先後順序非常重要, 當Android設備檢測到有NFC Tag靠近時,會根據Action申明的順序給對應的Activity 發送含NFC消息的 Intent.

2. Android NFC 消息發送機制

當Android設備檢測到有NFC Tag時,理想的行為是觸發最合適的Activity來處理檢測到的Tag,這是因為NFC通常是在非常近的距離才起作用(<4m) ,如果此時需要用戶來選擇合適的應用來處理Tag,很容易斷開與Tag之間的通信。因此你需要選擇合適的Intent filter 只處理你想讀寫的Tag類型。

Android系統支持兩種NFC消息發送機制:Intent 發送機制和前台Activity 消息發送機制。

Intent 發送機制 當系統檢測到Tag時,Android系統提供manifest 中定義的Intent filter 來選擇合適的Activity來處理對應的Tag,當有多個Activity可以處理對應的Tag類型時,則會顯示Activity選擇窗口由用戶選擇:

前台Activity 消息發送機制 允許一個在前台運行的Activity在讀寫NFC Tag 具有優先權,此時如果Android檢測到有NFC  Tag ,如果前台允許的Activity可以處理該種類型的Tag則該Activity具有優先權,而不出現Activity 選擇窗口。

這兩種方法基本上都是使用Intent-filter 來指明Activity可以處理的Tag類型,一個是使用Android的Manifest 來說明,一個是通過代碼來申明。

下圖顯示當Android檢測到Tag,消息發送的優先順序:

本例 NFCDemoActivity 支持兩種NFC消息發送機制,上面的XML指明了Intent 消息發送機制,其中

<meta-data android:name=”android.nfc.action.TECH_DISCOVERED”
android:resource=”@xml/filter_nfc”
/>

的filter_nfc 指明了支持處理的NFC Tag類型,filter_nfc.xml 定義如下:

<resources xmlns:xliff=”urn:oasis:names:tc:xliff:document:1.2″>
<!– capture anything using NfcF –>
<tech-list>
<tech>android.nfc.tech.NfcA</tech>
<tech>android.nfc.tech.MifareClassic</tech>
<tech>android.nfc.tech.MifareUltralight</tech>

</tech-list>

</resources>

因為我只有MifareClassic 類型的Tag,所以只定義了MifareClassic相關的Tag類型,如果你可以處理所有Android支持的NFC類型,可以定義為:

<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
    <tech-list>
        <tech>android.nfc.tech.IsoDep</tech>
        <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.Ndef</tech>
        <tech>android.nfc.tech.NdefFormatable</tech>
        <tech>android.nfc.tech.MifareClassic</tech>
        <tech>android.nfc.tech.MifareUltralight</tech>
    </tech-list>
</resources>

有了這個Manifest中的申明,當Android檢測到有Tag時,會顯示Activity選擇窗口,如上圖中的Reading Example。

當NFCDemoActiviy在前台運行時,我們希望只有它來處理Mifare 類型的Tag,此時可以使用前台消息發送機制,下面的代碼基本和ApiDemos中的NFC示例類似:

1public class NFCDemoActivity extendsActivity {
2 privateNfcAdapter mAdapter;
3 privatePendingIntent mPendingIntent;
4 privateIntentFilter[] mFilters;
5 privateString[][] mTechLists;
6 privateTextView mText;
7 privateint mCount = 0;
8  
9 @Override
10 publicvoid onCreate(Bundle savedState) {
11 super.onCreate(savedState);
12  
13 setContentView(R.layout.foreground_dispatch);
14 mText = (TextView) findViewById(R.id.text);
15 mText.setText("Scan a tag");
16  
17 mAdapter = NfcAdapter.getDefaultAdapter(this);
18  
19 // Create a generic PendingIntent that will be deliver
20 // to this activity. The NFC stack
21 // will fill in the intent with the details of the
22 //discovered tag before delivering to
23 // this activity.
24 mPendingIntent = PendingIntent.getActivity(this,0,
25 newIntent(this,
26    getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP),0);
27  
28 // Setup an intent filter for all MIME based dispatches
29 IntentFilter ndef
30    = new IntentFilter(NfcAdapter.ACTION_TECH_DISCOVERED);
31 try{
32 ndef.addDataType("*/*");
33 } catch (MalformedMimeTypeException e) {
34 thrownew RuntimeException("fail", e);
35 }
36 mFilters =new IntentFilter[] {
37 ndef,
38 };
39  
40 // Setup a tech list for all MifareClassic tags
41 mTechLists
42  = new String[][] { new String[] { MifareClassic.class.getName() } };
43 }
44  
45 @Override
46 publicvoid onResume() {
47 super.onResume();
48 mAdapter.enableForegroundDispatch(this,
49     mPendingIntent, mFilters, mTechLists);
50 }
51  
52 @Override
53 publicvoid onNewIntent(Intent intent) {
54 Log.i("Foreground dispatch",
55     "Discovered tag with intent: "+ intent);
56 mText.setText("Discovered tag "+
57       ++mCount +" with intent: " + intent);
58 }
59  
60 @Override
61 publicvoid onPause() {
62 super.onPause();
63 mAdapter.disableForegroundDispatch(this);
64 }
65}

只改了一行,將處理NfcF類型的Tag 改為處理MifareClassic 類型的NFC Tag。

mTechLists = new String[][] { new String[] { MifareClassic.class.getName() } };

運行該示例,每靠近一次Tag,計數加1.

前面例子介紹了檢測,讀寫NFC TAG開發的一般步驟,本例針對常用的Mifare Tag 具體說明。

Mifare Tag 可以有1K ,2K, 4K,其內存分區大同小異,下圖給出了1K位元組容量的Tag的內存分布:

數據分為16個區(Sector) ,每個區有4個塊(Block) ,每個塊可以存放16位元組的數據,其大小為16 X 4 X 16 =1024 bytes

每個區最後一個塊稱為Trailer ,主要用來存放讀寫該區Block數據的Key ,可以有A,B兩個Key,每個Key 長度為6個位元組,預設的Key值一般為全FF或是0. 由 MifareClassic.KEY_DEFAULT 定義。

因此讀寫Mifare Tag 首先需要有正確的Key值(起到保護的作用),如果鑒權成功

auth = mfc.authenticateSectorWithKeyA(j,
MifareClassic.KEY_DEFAULT);

然後才可以讀寫該區數據。

本例定義幾個Mifare相關的類 MifareClassCard ,MifareSector, MifareBlock 和MifareKey 以方便讀寫Mifare Tag.

Android 系統來檢測到NFC Tag, 將其封裝成Tag類,存放到Intent的NfcAdapter.EXTRA_TAG Extra 數據包中,可以使用MifareClassic.get(Tag) 獲取對象的 MifareClassic類。

1Tag tagFromIntent = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
2// 4) Get an instance of the Mifare classic card from this TAG
3// intent
4MifareClassic mfc = MifareClassic.get(tagFromIntent);

下面為讀取Mifare card 的主要代碼:

1// 1) Parse the intent and get the action that triggered this intent
2String action = intent.getAction();
3// 2) Check if it was triggered by a tag discovered interruption.
4if (NfcAdapter.ACTION_TECH_DISCOVERED.equals(action)) {
5 // 3) Get an instance of the TAG from the NfcAdapter
6 Tag tagFromIntent = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
7 // 4) Get an instance of the Mifare classic card from this TAG
8 // intent
9 MifareClassic mfc = MifareClassic.get(tagFromIntent);
10 MifareClassCard mifareClassCard=null;
11  
12 try{ // 5.1) Connect to card
13 mfc.connect();
14 booleanauth = false;
15 // 5.2) and get the number of sectors this card has..and loop
16 // thru these sectors
17 intsecCount = mfc.getSectorCount();
18 mifareClassCard=new MifareClassCard(secCount);
19 intbCount = 0;
20 intbIndex = 0;
21 for(int j = 0; j < secCount; j++) {
22 MifareSector mifareSector =new MifareSector();
23 mifareSector.sectorIndex = j;
24 // 6.1) authenticate the sector
25 auth = mfc.authenticateSectorWithKeyA(j,
26 MifareClassic.KEY_DEFAULT);
27 mifareSector.authorized = auth;
28 if(auth) {
29 // 6.2) In each sector - get the block count
30 bCount = mfc.getBlockCountInSector(j);
31 bCount =Math.min(bCount, MifareSector.BLOCKCOUNT);
32 bIndex = mfc.sectorToBlock(j);
33 for(int i = 0; i < bCount; i++) {
34  
35 // 6.3) Read the block
36 byte[]data = mfc.readBlock(bIndex);
37 MifareBlock mifareBlock =new MifareBlock(data);
38 mifareBlock.blockIndex = bIndex;
39 // 7) Convert the data into a string from Hex
40 // format.
41  
42 bIndex++;
43 mifareSector.blocks[i] = mifareBlock;
44  
45  
46 }
47 mifareClassCard.setSector(mifareSector.sectorIndex,
48 mifareSector);
49 } else { // Authentication failed - Handle it
50  
51 }
52 }
53 ArrayList<String> blockData=newArrayList<String>();
54 intblockIndex=0;
55 for(inti=0;i<secCount;i++){
56  
57 MifareSector mifareSector=mifareClassCard.getSector(i);
58 for(intj=0;j<MifareSector.BLOCKCOUNT;j++){
59 MifareBlock mifareBlock=mifareSector.blocks[j];
60 byte[]data=mifareBlock.getData();
61 blockData.add("Block "+ blockIndex++ +" : "+
62 Converter.getHexString(data, data.length));
63 }
64 }
65 String []contents=newString[blockData.size()];
66 blockData.toArray(contents);
67 setListAdapter(newArrayAdapter<String>(this,
68 android.R.layout.simple_list_item_1, contents));
69 getListView().setTextFilterEnabled(true);
70  
71 } catch (IOException e) {
72 Log.e(TAG, e.getLocalizedMessage());
73 showAlert(3);
74 }finally{
75  
76 if(mifareClassCard!=null){
77 mifareClassCard.debugPrint();
78 }
79 }
80}// End of method



  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值