Android訓練課程(Android Training) – NFC基礎
- 2020 年 3 月 13 日
- 筆記
NFC 基礎
本文檔介紹了在Android上的基本的NFC任務。它說明了如何發送和接收的NDEF消息(NDEF messages)的形式的表單里包含的NFC數據(NFC data),並介紹Android框架里支援這些功能的API。對於更高級的主題,包括與非NDEF數據的討論,請參閱高級NFC。
當使用NDEF 數據和Android時,有兩個主要的使用場景:
•從一個NFC 標籤里讀取NDEF 數據
•使用 Android Beam™ 快速傳輸Beaming NDEF messages從一台設備到另一台
<譯者註:Android Beam是android系統的一個傳輸套件,對於具有NFC設備的手機,可通過該功能在兩台手機之間傳輸聯繫人圖片等數據,使用方法也很簡單,將兩個手機背靠背,這時候位於上面的手機會提示你「敲擊螢幕即可穿數據」>
從一個NFC tag里讀取NDEF data 將會經過 tag dispatch system,分析被發現的NFC標籤,進行適當的數據歸類,並啟動一個對該分類的數據感興趣的應用程式。那些想要處理被掃描到的NFC標籤的應用程式可以聲明一個意圖過濾器(declare an intent filter ),並要求處理這些數據。
Android Beam™功能,它允許通過輕輕敲擊設備的方式,推送一個NDEF message從一台設備到另一台設備上。這種相互作用提供了一個簡單的方法來發送數據,比其它無線技術,比如藍牙,因為有了NFC,不再需要手動設置發現或配對(譯者註:藍牙需要設置搜索設備和配對)。當兩個設備進入範圍內(譯者註:NFC要求在幾厘米內),自動啟動連接。 Android Beam可通過一組NFC API被使用,因此,任何應用都可以在設備之間傳輸資訊。例如,聯繫人,瀏覽器和YouTube應用程式使用Android Beam與其他設備共享聯繫人,網頁和影片。
標籤分發系統 (The Tag Dispatch System)
Android的設備通常是在螢幕解鎖的時候尋找NFC標籤,除非在設置菜單中禁用了NFC設備。當Android手機發現了一個NFC標籤,所希望的行為是最適當的activity來處理它,而不是要求用戶選擇處理它的應用程式。因為設備掃描NFC標籤,在很短的範圍內,它很可能導致當用戶手動選擇(處理該tag的應用)時強行移動設備遠離標籤而斷開連接。您最好讓你開發的應用程式僅僅關注您制定的NFC標籤,以防止用戶手動選擇處理的活動的頁面出現。
為了幫助你實現這個目標,Android提供了一個特殊的標籤分發系統,它會分析被掃描到的NFC標籤,解析他們,並試圖定位到對這個被掃描到的標籤感興趣的應用程式。這是通過:
1。解析NFC標籤和搞清楚MIME類型,或者被包含在標籤中的有標記的一個URI。
2。先封裝MIME類型或URI,在裝入一個intent內。前兩個步驟中描述了NFC標籤是如何映射到MIME類型和URI的。
3。使用封裝好的intent啟動應用程式。這是描述如何將NFC標籤分派到對其感興趣的應用程式。
<譯者註:android 的標籤分發系統做了下面一些事情:解析標籤里的數據,並裝入intent內,並啟動關注該類型的標籤的應用程式>
NFC 標籤是如何被映射到 MIME 類型和URIs 的
NFC標籤是如何映射到MIME類型和URI
在你開始寫你的NFC應用之前,重要的是要了解不同類型的NFC標籤,標籤分發系統如何解析NFC標籤,當它(標籤分發系統)檢測到一個NDEF消息後如何分發到應用程式。NFC標籤是一種比較廣泛的技術(譯者註:標籤的種類樣式多),也有許多不同的數據寫入方式。 Android最大化的支援NDEF標準,它是由NFC論壇(NFC Forum.)定義的。
NDEF數據被封裝一個消息(NdefMessage)的內部,一個消息包含一個或多個的記錄(NdefRecord)。每個NDEF記錄必須是格式有效的,符合規範的。當然,你的NDEF記錄也可以符合你創建的類型的規範。 Android還支援其他不包含NDEF數據的標籤,您可以通過使用包含在android.nfc.tech包的類來實現它。要了解有關這些技術的更多資訊,請參見高級NFC主題。工作涉及到編寫自己的協議棧與這些其他類型的標籤進行通訊,因此我們建議在可能易於開發的情況下使用NDEF和採用Android的設備的最大支援。
注意:要下載完整的NDEF規格,請到NFC Forum Specification Download 頁面下載和查看 《創建一般類型的NDEF記錄Creating common types of NDEF records 》尋找如何建設NDEF記錄的示例。
現在,你有NFC標籤的一些背景知識,下面的章節更詳細描述了Android是如何處理NDEF格式的標籤的。當Android手機掃描一個包含了NDEF格式數據的NFC標籤,解析消息,並試圖找出數據的MIME類型或標識URI。要做到這一點,系統讀取Ndef Message裡面的第一個NdefRecord,以確定如何解釋整個NDEF消息(NDEF消息可以包含多個NDEF記錄)。在一個格式良好的NDEF消息中,第一個NdefRecord包含以下欄位:
3-bit TNF (Type Name Format) – 類型名格式
指示如何解釋變數長度類型欄位。有效的值記載在表1中描述的。
變數長度類型
描述記錄類型。如果使用TNF_WELL_KNOWN,使用此欄位指定的記錄類型定義(RTD)。有效的RTD值描述於表2中。
變數長度ID
記錄的唯一標識符。此欄位不經常使用,但如果您需要一個具有唯一標識的標籤,你可以創建一個ID來這麼做。
變數長度的有效載荷
要讀取或寫入的實際數據負載。 一個NDEF消息可以包含多個NDEF記錄,所以不要以為全部負載存在於這個NDEF消息的第一條NDEF紀錄內。
標籤分發系統使用TNF和類型欄位映射MIME類型或URI到一個NDEF消息。如果成功的話,它封裝的資訊位於一個ACTION_NDEF_DISCOVERED intent內部,連同那些實際的有效載荷。但是,也有標記調度系統不能從第一條NDEF記錄里確定數據的類型情。發生這種情況時,NDEF數據不能被映射到一個MIME類型或URI,或著當NFC標籤不以NDEF數據作為開始。在這種情況下,一個標籤對象(Tag object),該對象具有有關標籤內容的技術資訊和有效載荷,將會被封裝到一個ACTION_TECH_DISCOVERED intent內。
表1中。介紹了標籤分發系統如何映射TNF和類型欄位到MIME類型或URIs。同時也說明了哪些TNFs不能被映射到MIME類型或URI。在這種情況下,標籤分發系統將回退到ACTION_TECH_DISCOVERED的方式。
例如,如果標籤分發系統遇到一個TNF_ABSOLUTE_URI類型的紀錄,將該記錄的變數長度類型欄位映射到一個URI。標籤分發系統封裝封裝那個URI到一個 ACTION_NDEF_DISCOVERED intent的一個數據欄位內,以及與其他的標籤資訊,比如其他實際負載。另一方面,如果它遇到的記錄類型是TNF_UNKNOWN,它將創建標籤的技術資訊的封裝。
Table 1. 支援 TNFs and 對應的映射
Type Name Format (TNF)
Mapping
TNF_ABSOLUTE_URI
基於類型欄位的URI.
TNF_EMPTY
備選 ACTION_TECH_DISCOVERED.
TNF_EXTERNAL_TYPE
URI based on the URN in the type field. The URN is encoded into the NDEF type field in a shortened form: <domain_name>:<service_name>. Android maps this to a URI in the form:vnd.android.nfc://ext/<domain_name>:<service_name>.
TNF_MIME_MEDIA
基於類型欄位的MIME 類型。
TNF_UNCHANGED
使得第一條記錄無效, 於是回退到 ACTION_TECH_DISCOVERED.
TNF_UNKNOWN
備選 ACTION_TECH_DISCOVERED.
TNF_WELL_KNOWN
MIME 類型 或者 URI 依賴於 記錄類型定義( Record Type Definition RTD), 這是您設置的類型欄位. 參考 Table 2. 獲得更多的資訊關於 有效的 RTDs 和 對應的映射.
Table 2. 支援 RTDs 關於 TNF_WELL_KNOWN 和對應的 映射
Record Type Definition (RTD)
Mapping
RTD_ALTERNATIVE_CARRIER
回退到 ACTION_TECH_DISCOVERED.
RTD_HANDOVER_CARRIER
回退到 ACTION_TECH_DISCOVERED.
RTD_HANDOVER_REQUEST
回退到 ACTION_TECH_DISCOVERED.
RTD_HANDOVER_SELECT
回退到 ACTION_TECH_DISCOVERED.
RTD_SMART_POSTER
需要對負載的內容進行解析的URI
RTD_TEXT
MIME 類型 的 text/plain.
RTD_URI
負載的內容是URI
NFC標籤是怎樣被分派到應用程式的
當標籤分發系統創建了一個封裝了NFC標籤和它的識別資訊的 intent,標籤分發系統會將該intent發送到添加了該intent 的過濾器(intent filter)的應用程式。如果一個以上的應用程式可以處理的該intent,活動選擇器將被啟動,使得用戶可以選擇哪一個應用程式。標籤調度系統定義了三種intent,下面從最高優先順序到最低優先順序的順序展示它們:
ACTION_NDEF_DISCOVERED:當一個標籤被掃描,包含有NDEF有效載荷或者是一個可識別的類型,將會創建一個這樣的intent 用來啟動一個應用程式。這是最高優先順序的意圖,標籤分發系統會儘可能的使用這種intent而不使用其他方式來啟動一個應用。<譯者註:儘可能直接對應一個應用而防止出現讓用戶選擇活動> ACTION_TECH_DISCOVERED:如果沒有活動註冊處理ACTION_NDEF_DISCOVERED intent,標籤分發系統嘗試用這個意圖啟動應用程式。如果標籤被掃描包含有NDEF數據但不能被映射到一個MIME類型或URI,或者如果標籤不包含NDEF數據但同時是一個已知的標籤技術,此intent也可直接被啟動(沒有ACTION_NDEF_DISCOVERED優先)。 ACTION_TAG_DISCOVERED:如果沒有活動處理ACTION_NDEF_DISCOVERED 或者 ACTION_TECH_DISCOVERED的inent,那麼這個inent將被啟動。. 標籤分發系統的工作原理,基本方法如下:
標籤分發系統解析NFC的標籤,創建inten,嘗試啟動一個應用程式(無論是ACTION_NDEF_DISCOVERED或ACTION_TECH_DISCOVERED)的。 如果沒有應用的過濾器(intent filter)捕獲它,嘗試使用下一個低優先順序的intent啟動一個應用程式(eitherACTION_TECH_DISCOVERED或ACTION_TAG_DISCOVERED),直到被應用程式的過濾器捕獲,或者直到標籤分發度系統嘗試了所有可能的intent。 如果沒有應用程式的intent過濾器(inent filter)捕獲該intent,什麼也不做. <譯者註:android可以在系統配置文件指定某個activity的intent filter,標籤分發系統會嘗試構建intent來直接對應它,如果失敗,就再次嘗試更低級別的inent來嘗試,直到發現沒有任何一個來處理它>
圖 1. 標籤分發系統
只要有可能,請使用NDEF消息和ACTION_NDEF_DISCOVERED的inent來達成你的工作,因為它是三種inent里最特殊的一個。相比其他兩種intent,使用這個intent,您可以啟動您的應用程式在一個更合適的時間,從而給用戶帶來更好的體驗.
<譯者注:因為ACTION_NDEF_DISCOVERED intent比較特殊,才更加的不容易發生用戶不得不手動選擇處理該標籤的程式的情況出現>
在Android清單文件(Manifest)里要求NFC訪問
在您訪問NFC硬體設備和妥善處理NFC的intent之前,在你的AndroidManifest.xml文件里聲明這些項:
•NFC的硬體訪問許可權<uses-permission>:
<uses-permission android:name="android.permission.NFC"/>
•您的應用程式可以支援的最低SDK版本。 API級別9只支援通過ACTION_TAG_DISCOVERED有限的標籤分發,只給訪問NDEF的消息使用EXTRA_NDEF_MESSAGES進行額外的擴展。沒有其他變數的屬性或I/ O操作可以訪問。 API 10級包括全面的讀/寫支援以及前台NDEF推入, API 14級提供了一個簡單的使用Android Beam 對NDEF消息到其他設備的方式來推入和額外的方便的方法創建NDEF記錄。
<uses-sdk android:minSdkVersion="10"/>
用戶特性 uses-feature 元素的聲明,使得當你的應用程式Google Play 里出現時,僅僅被具有NFC硬體的手機設備看到: <uses-feature android:name="android.hardware.nfc" android:required="true" />
如果你的程式里的NFC功能不是必須得主要功能,那麼可以忽略上面這個用戶特性 uses-feature 。你可以在程式運行時檢測是否具有NFC硬體設備,使用getDefaultAdapter() 方法,如果為空,就是沒有NFC設備。.
NFC Intents的過濾器
一個你關注的NFC標籤進行被掃描盜時,您的應用程式啟動處理,您的應用程式可以在清單文件中聲明過濾一種,兩種或全部三種NFC intent。但是,當應用程式啟動時,您通常要篩選ACTION_NDEF_DISCOVERED意圖為了更多的控制。ACTION_TECH_DISCOVERED意圖是一個備用的方式,當ACTION_NDEF_DISCOVERED時沒有被註冊到過濾器,或者有效載荷數據不是NDEF數據時。過濾ACTION_TAG_DISCOVERED的intnet屬於過於籠統的過濾器類別。許多應用程式會在ACTION_TAG_DISCOVERED之前,先過濾ACTION_NDEF_DISCOVERED或ACTION_TECH_DISCOVERED,所以您的應用程式啟動的概率很低。ACTION_TAG_DISCOVERED僅可作為最後的手段,僅僅在沒有其他過濾了ACTION_NDEF_DISCOVERED或ACTION_TECH_DISCOVERED的意圖的應用程式被安裝的情況下才有用。
因為NFC標籤的多樣化和多次性,往往不在你的控制之下,這並不總是可能的,這就是為什麼你在必要時需要備選其他兩種意圖。當你需要控制標籤的類型和數據寫入,建議您使用NDEF格式的標籤。以下各節描述了如何過濾每種類型的意圖.
ACTION_NDEF_DISCOVERED
為了過濾ACTION_NDEF_DISCOVERED intents, 聲明一個過濾器並且指定數據類型. 下面演示了如何過濾 ACTION_NDEF_DISCOVERED intents,它的MIME類型是text/plain:
<intent-filter> <action android:name="android.nfc.action.NDEF_DISCOVERED"/> <category android:name="android.intent.category.DEFAULT"/> <data android:mimeType="text/plain" /> </intent-filter>
下面演示了URI格式的過濾,過濾格式為 http://developer.android.com/index.html.
<intent-filter> <action android:name="android.nfc.action.NDEF_DISCOVERED"/> <category android:name="android.intent.category.DEFAULT"/> <data android:scheme="http" android:host="developer.android.com" android:pathPrefix="/index.html" /> </intent-filter>
ACTION_TECH_DISCOVERED
如果你的程式想要過濾 ACTION_TECH_DISCOVERED intent, 你必須創建一個xml文件來描述你的程式支援的標籤類型的規範,這個規範文件里包含你支援的最小的技術列表tech-list. 這個列表支援的技術,將會和標籤支援的技術進行匹配。在程式運行時,你可以獲得這些內容,通過調用getTechList()方法。
比如,一個被掃描到的標籤支援 MifareClassic, NdefFormatable, and NfcA, 你的程式里的支援列表裡需要指明支援其中的三種,兩種,其中一種技術(或者可能都沒有) 為了使得你的程式被匹配到.
下面的示例定義了支援的所有的技術. 你可以移除那些你不要的. 保存到 (可隨意命名) in the <project-root>/res/xml 文件夾.
<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>
你可以指定多個 tech-list 集合. 每一個 tech-list 被認為是獨立的, 並且你的程式將會被匹配到一個單一的 tech-list , 它可以通過 getTechList()返回結果. 它為了技術匹配提供了 AND and OR 語義. 下面演示了一個標籤匹配支援NfcA and Ndef 技術 或者 可被支援 NfcB and Ndef 技術:
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <tech-list> <tech>android.nfc.tech.NfcA</tech> <tech>android.nfc.tech.Ndef</tech> </tech-list> </resources>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <tech-list> <tech>android.nfc.tech.NfcB</tech> <tech>android.nfc.tech.Ndef</tech> </tech-list> </resources>
在你的 AndroidManifest.xml 文件, 指定你剛剛創建的資源文件的位置。 在<activity> 節點的 <meta-data> 節點下 ,下面是演示:
<activity> … <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>
想獲得更多標籤的技術支援資訊和 關於 ACTION_TECH_DISCOVERED intent,查閱Working with Supported Tag Technologies 在NFC高級篇.
ACTION_TAG_DISCOVERED
為了過濾 ACTION_TAG_DISCOVERED 使用下面的方式:
<intent-filter> <action android:name="android.nfc.action.TAG_DISCOVERED"/> </intent-filter>
從 intents 中讀取資訊
如果NFC intent啟動一個應用, 你可以從這個 intent中獲得更多資訊. 該Intents可以從標籤里讀到到下列擴展資訊:
EXTRA_TAG (必選): 一個代表里讀取到的標籤的 Tag 對象. EXTRA_NDEF_MESSAGES (可選): 從標籤的 NDEF messages 中讀取到的一個數據集合. 這個資訊是強制的。 {@link android.nfc.NfcAdapter#EXTRA_ID (可選): 標籤的低級別的ID. 要獲得這些擴展資訊,請檢查如果您的程式是否被NFC intent啟動,並確保一個標籤被掃描,這時就可以從intent中讀取擴展資訊了。下面的示例檢查了一個ACTION_NDEF_DISCOVERED的intent,並嘗試從包含NDEF消息的intent中讀取擴展資訊。.
public void onResume() { super.onResume(); … if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(getIntent().getAction())) { Parcelable[] rawMsgs = intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES); if (rawMsgs != null) { msgs = new NdefMessage[rawMsgs.length]; for (int i = 0; i < rawMsgs.length; i++) { msgs[i] = (NdefMessage) rawMsgs[i]; } } } //process the msgs array }
另外,你也可以從intent得到一個tag對象,它將包含有效載荷數據,並允許您列舉標籤的技術:
Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
創建常見類型的NDEF紀錄
本節介紹如何創建一個通用類型的NDEF記錄,有助於你寫入到NFC標籤,或發送數據與Android Beam。開始採用Android4.0(API級別14)中,createUri()方法可以幫助你自動創建URI類型的記錄。在Android4.1(API16級),createExternal()和createMime()是可幫助您創建MIME類型和外部擴展類型NDEF記錄。使用這些輔助方法使得在手動創建NDEF記錄時儘可能避免錯誤。
本節還介紹了如何創建相應的intent filter的記錄。下面這些NDEF記錄示例必須在你的NDEF消息里的第一條NDEF記錄內,在你寫入標籤或者Android Beam時.
TNF_ABSOLUTE_URI
注意: 我們推薦你使用 RTD_URI 類型來代替 TNF_ABSOLUTE_URI, 因為它效率更高.
你可以創建一個 TNF_ABSOLUTE_URI NDEF 記錄,按照下面的方式:
NdefRecord uriRecord = new NdefRecord( NdefRecord.TNF_ABSOLUTE_URI , "http://developer.android.com/index.html".getBytes(Charset.forName("US-ASCII")), new byte[0], new byte[0]);
過濾上面這個NDEF記錄的intent filter這麼寫:
<intent-filter> <action android:name="android.nfc.action.NDEF_DISCOVERED" /> <category android:name="android.intent.category.DEFAULT" /> <data android:scheme="http" android:host="developer.android.com" android:pathPrefix="/index.html" /> </intent-filter>
TNF_MIME_MEDIA
你可以創建一個 TNF_MIME_MEDIA NDEF 記錄, 按下面的方式.
使用 createMime() 方法:
NdefRecord mimeRecord = NdefRecord.createMime("application/vnd.com.example.android.beam", "Beam me up, Android".getBytes(Charset.forName("US-ASCII")));
手動方式創建 NdefRecord :
NdefRecord mimeRecord = new NdefRecord( NdefRecord.TNF_MIME_MEDIA , "application/vnd.com.example.android.beam".getBytes(Charset.forName("US-ASCII")), new byte[0], "Beam me up, Android!".getBytes(Charset.forName("US-ASCII")));
過濾上面這個NDEF記錄的intent filter這麼寫:
<intent-filter> <action android:name="android.nfc.action.NDEF_DISCOVERED" /> <category android:name="android.intent.category.DEFAULT" /> <data android:mimeType="application/vnd.com.example.android.beam" /> </intent-filter>
TNF_WELL_KNOWN with RTD_TEXT
你可以創建一個 TNF_WELL_KNOWN NDEF 記錄,按照下面的方式:
public NdefRecord createTextRecord(String payload, Locale locale, boolean encodeInUtf8) { byte[] langBytes = locale.getLanguage().getBytes(Charset.forName("US-ASCII")); Charset utfEncoding = encodeInUtf8 ? Charset.forName("UTF-8") : Charset.forName("UTF-16"); byte[] textBytes = payload.getBytes(utfEncoding); int utfBit = encodeInUtf8 ? 0 : (1 << 7); char status = (char) (utfBit + langBytes.length); byte[] data = new byte[1 + langBytes.length + textBytes.length]; data[0] = (byte) status; System.arraycopy(langBytes, 0, data, 1, langBytes.length); System.arraycopy(textBytes, 0, data, 1 + langBytes.length, textBytes.length); NdefRecord record = new NdefRecord(NdefRecord.TNF_WELL_KNOWN, NdefRecord.RTD_TEXT, new byte[0], data); return record; }
過濾上面這個NDEF記錄的intent filter這麼寫:
<intent-filter> <action android:name="android.nfc.action.NDEF_DISCOVERED" /> <category android:name="android.intent.category.DEFAULT" /> <data android:mimeType="text/plain" /> </intent-filter>
TNF_WELL_KNOWN with RTD_URI
你可以創建一個 TNF_WELL_KNOWN NDEF 記錄,按照下面的方式:
使用 createUri(String) 方法:
NdefRecord rtdUriRecord1 = NdefRecord.createUri("http://example.com");
使用 createUri(Uri) 方法:
Uri uri = new Uri("http://example.com"); NdefRecord rtdUriRecord2 = NdefRecord.createUri(uri);
手動創建 NdefRecord :
byte[] uriField = "example.com".getBytes(Charset.forName("US-ASCII")); byte[] payload = new byte[uriField.length + 1]; //add 1 for the URI Prefix byte payload[0] = 0x01; //prefixes http://www. to the URI System.arraycopy(uriField, 0, payload, 1, uriField.length); //appends URI to payload NdefRecord rtdUriRecord = new NdefRecord( NdefRecord.TNF_WELL_KNOWN, NdefRecord.RTD_URI, new byte[0], payload);
過濾上面這個NDEF記錄的intent filter這麼寫:
<intent-filter> <action android:name="android.nfc.action.NDEF_DISCOVERED" /> <category android:name="android.intent.category.DEFAULT" /> <data android:scheme="http" android:host="example.com" android:pathPrefix="" /> </intent-filter>
TNF_EXTERNAL_TYPE
你可以創建一個 TNF_EXTERNAL_TYPE NDEF 記錄,按照下面的方式:
使用 createExternal() 方法:
byte[] payload; //assign to your data String domain = "com.example"; //usually your app's package name String type = "externalType"; NdefRecord extRecord = NdefRecord.createExternal(domain, type, payload);
手動創建 NdefRecord :
byte[] payload; … NdefRecord extRecord = new NdefRecord( NdefRecord.TNF_EXTERNAL_TYPE, "com.example:externalType", new byte[0], payload);
The intent filter for the previous NDEF records would look like this:
<intent-filter> <action android:name="android.nfc.action.NDEF_DISCOVERED" /> <category android:name="android.intent.category.DEFAULT" /> <data android:scheme="vnd.android.nfc" android:host="ext" android:pathPrefix="/com.example:externalType"/> </intent-filter>
使用 TNF_EXTERNAL_TYPE 類型是一個更普遍的標籤,可以更好的支援android和非android設備.
注意: 關於TNF_EXTERNAL_TYPE 的URNs 有典型格式:urn:nfc:ext:example.com:externalType, 然而 NFC Forum RTD 規範聲明: urn:nfc:ext: 部分必須從NDEF記錄里被省略. 那麼,所有你需要提供的內容是一個域 (比如example.com ) 和類型(比如externalType),並用冒號分割它們. 當標籤分發系統分發一個TNF_EXTERNAL_TYPE的標籤時, Android 轉換urn:nfc:ext:example.com:externalType 格式的URN 成vnd.android.nfc://ext/example.com:externalType 格式的URI, 它就是上面的intent過濾器里聲明的演示的樣子.
Android 應用程式記錄 (AAR)
Android在Android4.0(API等級14)推出一個Android應用程式記錄(AAR),AAR提供了更強的確定性,在您的應用程式因為一個NFC標籤被掃描時而啟動時。 AAR具有嵌入在NDEF記錄內的應用程式的包名。您可以添加一個AAR到您的任何NDEF記錄NDEF消息內,因為Android搜索整個NDEF消息內的所有AAR。如果它發現AAR,根據裡面的AAR的包名稱來啟動應用程式。如果設備上不存在該應用程式,GooglePLAY(譯者註:應用程式市場)將被啟動並導向去下載該應用程式。.
如果你要防止其他應用程式過濾功能可能相同的意圖和潛在的處理您已經部署的特定標籤,AARs是有用的。因為包名AARS只支援在應用程式級別過濾,而不是在通過使用intetnt過濾器的Activity級別。如果你想處理一個Activity的intent,請使用intent filter。
如果一個標籤包含一個AAR,標籤分發系統將會以下方式工作:.
嘗試按常規的方式使用intent filter過濾器來啟動一個activity. 如果該應用匹配了過濾器的規則,同時又匹配了AAR的規則,那麼啟動該activity. 如果匹配了過濾器的規則,但是未匹配AAR規則;如果一個intent引發多個activity的啟動(譯者註:會彈出提示用戶手動選擇);或者沒有任何一個activity過濾到一個intent;那麼將按照AAR程式規範執行. 如果沒有應用程式可以被AAR啟動,那麼將被引導到Google Play 以下載AAR指向的應用程式。 注意: 你可以使用 前端分發系統( foreground dispatch system)來覆蓋AARs和標籤分發系統的處理方式, 它允許一個前端正在運行的頂層活動的activity 擁有更高的優先權來處理髮現到的標籤. 使用這種方法時,必須要在前端頂層主動的覆蓋AARs和標籤分發系統的行為.
如果你仍然想過濾那些不包含AAR的標籤,你可以按常規形式聲明一個intent filter. 在你的應用程式對其他不包含AAR的標籤有興趣時,這將很有用. 比如, 或許你想保證你的程式 處理 你不熟的私有標籤 as well as 第三方部署的常規標籤. 保持關注 Android 4.0 及以後的設備規範, 這樣當部署時, 你非常可能想去使用一個組合AARs 和 MIME 類型的/URIs 為了支援更寬廣範圍的設備. 另外,當你部署 NFC 標籤, 考慮一下如果寫入你的NFC標籤捨得可以支援更多的設備(Android設備和其他設備). 你可以這樣做,像定義相對唯一的 MIME 類型 或URI 的方式來使得更容易區分.
Android 提供了一個簡單的 API去創建一個AAR, createApplicationRecord(). 你所做的就是在任何位置嵌入一個AAR 到你的Ndef消息內. 你不需要使用你的ndef消息的第一條記錄, 除非你僅僅只有一條記錄. 這是因為 Android 系統 檢測 Ndef消息的第一條記錄來決定 MIME 類型 或者 標籤的URI, 它常常用於為應用程式過濾intent時創建一個intent. 下面的程式碼演示了如果創建AAR:
NdefMessage msg = new NdefMessage( new NdefRecord[] { …, NdefRecord.createApplicationRecord("com.example.android.beam")}
Beaming NDEF消息到其他設備
Android Beam可以支援兩款Android設備之間的簡單的點到點數據交換。想要進行Android Beam數據到另一台設備中的那台的設備的應用程式必須是在前台程式(譯者註:活動的),而且接收該數據的設備不能被鎖定。當準備進行Android Beam的設備有足夠接近的接觸到準備接收的設備時,Android Beam設備顯示「點擊螢幕開始Beam」的窗體。然後,用戶可以選擇是否Beam消息到接收設備內。.
注意: 前端 NDEF 推送 在API level 10後 是可用的, 它提供了和 Android Beam.類似的功能。那些 APIs 目前已經被棄用, 但是在舊的設備仍然是可用的. 閱讀 enableForegroundNdefPush() 可以獲得更多資訊.
你可以在你的程式里啟用 Android Beam ,使用下面兩個方法中的任何一個:
setNdefPushMessage(): 接受一個 NdefMessage,作為準備進行beam的消息. 當兩個設備非常接近時,自動的進行beams 消息 . setNdefPushMessageCallback(): 接受一個回調方法,該回調包含了一個 createNdefMessage()方法, 該回調在一個設備在可以beam 的時候被調用. 這個回調方法允許你僅僅在需要的時候創建Ndef消息. 一個Activity 一次只能推送一個NDEF消息,所以如果兩者都設置了,setNdefPushMessageCallback()的優先順序超過setNdefPushMessage()。使用Android Beam,必須滿足以下一般原則: 進行beaming數據的 activity 必須在前端. 兩個設備的螢幕都不能被鎖定. 你必須封裝你準備 beaming的數據到一個 NdefMessage 消息對象. 在NFC的裝置,接收的無線傳輸數據必須支援com.android.npp的NDEF的推送協議或NFC論壇的SNEP(簡單的NDEF交換協議)。該com.android.npp協議需要API 9級的Android 2.3設備,直到 API 13級Android 3.2的設備。 com.android.npp和SNEP都是必需的API14級(Android4.0)和更高版本。 注意: 如果你的activity 啟用了 Android Beam 並且當前處於前端活動的, 標準的標籤分發系統將不可使用. 然而, 如果你的activity 仍然啟用的前端分發系統(foreground dispatching), 這是它仍然能匹配標籤,在你i的標籤分發系統里匹配intent filters設置.
啟用 Android Beam 的方法:
創建一個 NdefMessage,這個消息里包含了你準備推送到其他設備的 NdefRecord . 在你的activity的onCreate()方法中,調用 setNdefPushMessage() 傳入參數是一個 NdefMessage 或 者調用 setNdefPushMessageCallback 傳入一個 NfcAdapter.CreateNdefMessageCallback 對象. 這些方法都至少需要一個你要啟用Android Beam的activity,連同其他激活的activity的可選列表。 在一般情況下,當兩個設備都在溝通的範圍,如果僅僅推送相同的NDEF消息,您通常使用setNdefPushMessage()。當您的應用程式關心當前應用程式的上下文資訊(目前情況),或者希望根據用戶在做什麼來推動一個NDEF消息,您可以使用 setNdefPushMessageCallback。
下面的示例顯示了如何在一個簡單的activity的onCreate()方法中調用NfcAdapter.CreateNdefMessageCallback(完整的示例見AndroidBeamDemo)。這個例子也可以幫助您創建一個MIME記錄:
package com.example.android.beam;
import android.app.Activity; import android.content.Intent; import android.nfc.NdefMessage; import android.nfc.NdefRecord; import android.nfc.NfcAdapter; import android.nfc.NfcAdapter.CreateNdefMessageCallback; import android.nfc.NfcEvent; import android.os.Bundle; import android.os.Parcelable; import android.widget.TextView; import android.widget.Toast; import java.nio.charset.Charset;
public class Beam extends Activity implements CreateNdefMessageCallback { NfcAdapter mNfcAdapter; TextView textView;
@Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); TextView textView = (TextView) findViewById(R.id.textView); // Check for available NFC Adapter mNfcAdapter = NfcAdapter.getDefaultAdapter(this); if (mNfcAdapter == null) { Toast.makeText(this, "NFC is not available", Toast.LENGTH_LONG).show(); finish(); return; } // Register callback mNfcAdapter.setNdefPushMessageCallback(this, this); } @Override public NdefMessage createNdefMessage(NfcEvent event) { String text = ("Beam me up, Android!nn" + "Beam Time: " + System.currentTimeMillis()); NdefMessage msg = new NdefMessage( new NdefRecord[] { createMime( "application/vnd.com.example.android.beam", text.getBytes()) /** * The Android Application Record (AAR) is commented out. When a device * receives a push with an AAR in it, the application specified in the AAR * is guaranteed to run. The AAR overrides the tag dispatch system. * You can add it back in to guarantee that this * activity starts when receiving a beamed message. For now, this code * uses the tag dispatch system. */ //,NdefRecord.createApplicationRecord("com.example.android.beam") }); return msg; } @Override public void onResume() { super.onResume(); // Check to see that the Activity started due to an Android Beam if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(getIntent().getAction())) { processIntent(getIntent()); } } @Override public void onNewIntent(Intent intent) { // onResume gets called after this to handle the intent setIntent(intent); } /** * Parses the NDEF Message from the intent and prints to the TextView */ void processIntent(Intent intent) { textView = (TextView) findViewById(R.id.textView); Parcelable[] rawMsgs = intent.getParcelableArrayExtra( NfcAdapter.EXTRA_NDEF_MESSAGES); // only one message sent during the beam NdefMessage msg = (NdefMessage) rawMsgs[0]; // record 0 contains the MIME type, record 1 is the AAR, if present textView.setText(new String(msg.getRecords()[0].getPayload())); }
}
注意,這段程式碼注釋掉AAR,您也可以刪除它。如果您啟用了AAR,在AAR指定的應用程式總是能接收Android Beam消息。如果應用程式是不存在的,Google Play會啟動下載該應用程式。因此,Android4.0或更高版本的設備下,如果使用的AAR,下面的意圖過濾器不是技術上必須的:
<intent-filter> <action android:name="android.nfc.action.NDEF_DISCOVERED"/> <category android:name="android.intent.category.DEFAULT"/> <data android:mimeType="application/vnd.com.example.android.beam"/> </intent-filter>
有了這個意圖過濾器,現在當它掃描一個NFC標籤,或接收到一個AAR的類型com.example.android.beam的android beam消息時,或當收到一個包含了 MIME 記錄application/vnd.com.example.android.beam.記錄的NDEF格式的消息,現在的com.example.android.beam應用程式將可以被啟動。。
即使AARS保證了應用程式被啟動或下載,仍然建議使用意圖過濾器。因為它可以讓你啟動您選擇的應用程式中的Activity,而不是總是啟動一個AAR指定的包內的主Activity。AARs沒有Activity級別水平的粒度。同時,由於一些Android系統的設備不支援AARS,以防萬一,你也應該嵌入的標識資訊在你的NDEF消息的第一條記錄里,並且使用過濾器過濾它。關於如何創建記錄的更多資訊,請參閱Creating Common Types of NDEF records 。
本文翻譯自Google的文檔,轉載請註明出處。原文地址:
http://developer.android.com/intl/zh-CN/guide/topics/connectivity/nfc/nfc.html
本人藉助google 翻譯,有道詞典進行的翻譯,感謝這些方便的工具提供者們。
同時感謝cnblogs提供的部落格功能。
本文的預期讀者是具有一些android開發基礎的開發人員,在了解開發的知識下,應該能讀懂文章的內容。
本人能力有限,翻譯會有疏漏和錯誤,還請讀者諒解。
翻譯這些內容來源於項目的需要,曾經接到需要讀取RFID標籤的一個需求。後來到處在網上搜說一些文章,直到發現這 android文檔提供的詳盡內容,決定用一些時間來翻譯它,這樣自己閱讀也方便,同時也方便那些尋找資料的開發人員。
正文如下: