本文介紹在Android系經過你所能執行的基本任務。它解釋瞭如何用NDEF消息格式來發送和接收NFC數據,而且介紹了支持這些功能的Android框架API。有關更高級的話題,包括對非NDEF格式數據的討論,狀況「高級 NFC」html
NDEF數據和Android一塊兒工做的場景主要有兩個:java
1. 從NFC標籤中讀取NDEF數據;android
2. 把NDEF消息從一個設備發送給另外一個設備。數組
從NFC標籤中讀取NDEF數據是用標籤調度系統來處理的,它會分析被發現的NFC標籤,對數據進行適當的分類,並啓動對該類數據感興趣的應用程序。想要處理被掃描到NFC標籤的應用程序會聲明一個Intent過濾器,並請求處理數據。瀏覽器
Android Beam™ 功能容許設備把一個NDEF消息推送到物理上相互監聽的另外一個設備上。這種交互提供了比其餘無線技術(如藍牙)更容易的發送數據的方法。由於NFC不須要手動的設備發現或配對要求。兩個設備在接近到必定範圍時會自動的鏈接。Android Beam經過一組NFC API來使用,以便應用程序可以在設備之間來傳輸信息。例如,通訊錄、瀏覽器以及YouTube等應用程序都使用Android Beam來跟其餘設備共享通訊錄、網頁和視頻。app
NFC標籤調度系統框架
一般,除非是在設備的設置菜單中NFC被禁用,不然Android設備會在非鎖屏的狀態下搜索NFC。當Android設備發現NFC標籤時,指望的行爲是用最合適的Activity來處理該Intent,而不是詢問用戶使用什麼應用程序。由於設備只能在很短的範圍內掃描到NFC標籤,強制的讓用戶手動的選擇一個Activity,會致使設備離開NFC標籤,從而中斷該鏈接。你應該開發你本身的Activity來處理你所關心的NFC標籤,從而阻止 選擇器的操做。dom
爲了幫助你達到這個目標,Android提供了特殊的標籤調度系統,來分析掃描到的NFC標籤,經過解析數據,在被掃描到的數據中嘗試找到感興趣的應用程序,具體作法以下:ide
1. 解析NFC標籤並搞清楚標籤中標識數據負載的MIME類型或URI;post
2. 把MIME類型或URI以及數據負載封裝到一個Intent中。
3. 基於Intent來啓動Activity。
怎樣把NFC標籤映射到MIME類型和URI
開始編寫NFC應用程序以前,重要的是要理解不一樣類型的NFC標籤、標籤調度系統是如何解析NFC標籤的、以及在檢測到NDEF消息時,標籤調度系統所作的特定的工做等。NFC標籤涉及到普遍的技術,而且有不少不一樣的方法向標籤中寫入數據。Android支持由NFC Forum所定義的NDEF標準。
NDEF數據被封裝在一個消息(NdefMessage)中,該消息中包含了一條或多條記錄(NdefRecord)。每一個NDEF記錄必須具備良好的你想要建立的記錄類型的規範的格式。Android也支持其餘的不包含NDEF數據類型的標籤,你可以使用android.nfc.tech包中的類來工做。要使用其餘類型標籤來工做,涉及到編寫本身的跟該標籤通訊的協議棧,所以咱們建議你儘量的使用NDEF,以便減小開發難度,而且最大化的支持Android設備。
注意:要下載完整的NDEF規範,請去「NFC論壇規範下載」網址來下載。
如今,你已經具有了一些NFC標籤的背景知識,接下來要詳細的介紹Android是如何處理NDEF格式的標籤的。當Android設備掃描到包含NDEF格式數據的NFC標籤時,它會解析該消息,並嘗試搞清楚數據的MIME類型或URI標識。首先系統會讀取消息(NdefMessage)中的第一條NdefRecord,來判斷如何解釋整個NDEF消息(一個NDEF消息可以有多條NDEF記錄)。在格式良好的NDEF消息中,第一條NdefRecord包含如下字段信息:
3-bit TNF(類型名稱格式)
指示如何解釋可變長度類型字段,在下表1中介紹有效值。
可變長度類型
說明記錄的類型,若是使用TNF_WELL_KNOWN,那麼則使用這個字段來指定記錄的類型定義(RTD)。在下表2中定義了有效的RTD值。
可變長度ID
惟一標識該記錄。這個字段不常用,可是,若是須要惟一的標識一個標記,那麼就能夠爲該字段建立一個ID。
可變長度負載
你想讀/寫的實際的數據負載。一個NDEF消息可以包含多個NDEF記錄,所以不要覺得在NDEF消息的第一條NDEF記錄中包含了全部的負載。
標籤調度系統使用TNF和類型字段來嘗試把MIME類型或URI映射到NDEF消息中。若是成功,它會把信息跟實際的負載一塊兒封裝到ACTION_NEDF_DISCOVERED類型的Intent中。可是,會有標籤調度系統不能根據第一條NDEF記錄來判斷數據類型的狀況,這樣就會有NDEF數據不能被映射到MIME類型或URI,或者是NFC標籤沒有包含NDEF開始數據的狀況發生。在這種狀況下,就會用一個標籤技術信息相關的Tag對象和封裝在ACTION_TECH_DISCOVERED類型Intent對象內部的負載來代替。
表1.介紹標籤調度系統映射如何把TNF和類型字段映射到MIME型或URI上。同時也介紹了那種類型的TNF不能被映射到MIME類型或URI上。這種狀況下,標籤調度系統會退化到ACTION_TECH_DISCOVERED類型的Intent對象。
例如,若是標籤調度系統遇到一個TNF_ABSOLUTE_URI類型的記錄,它會把這個記錄的可變長度類型字段映射到一個URI中。標籤調度系統會把這個URI跟其餘相關的標籤的信息(如數據負載)一塊兒封裝到ACTION_NDEF_DISCOVERED的Intent對象中。在另外一方面,若是遇到了TNF_UNKNOWN類型,它會建立一個封裝了標籤技術信息的Intent對象來代替。
表1.所支持的TNF和它們的映射
類型名稱格式(TNF) |
映射 |
TNF_ABSOLUTE_URI |
基於類型字段的URI |
TNF_EMPTY |
退化到ACTION_TECH_DISCOVERED類型的Intent對象 |
TNF_EXTERNAL_TYPE |
基於類型字段中URN的URI。URN是縮短的格式(<domain_name>:<service_name)被編碼到NDEF類型中。Android會把這個URN映射成如下格式的URI:vnd.android.nfc://ext/<domain_name>:<service_name>。 |
TNF_MIME_MEDIA |
基於類型字段的MIME類型 |
TNF_UNCHANGED |
退化到ACTION_TECH_DISCOVERED類型的Intent對象 |
TNF_UNKNOWN |
退化到ACTION_TECH_DISCOVERED類型的Intent對象 |
TNF_WELL_KNOWN |
依賴你在類型字段中設置的記錄類型定義(RTD)的MIME類型或URI, |
表2.TNF_WELL_KNOWN所支持的RTD和它們的映射
記錄類型定義(RTD) |
映射 |
RTD_ALTERNATIVE_CARRIER |
退化到ACTION_TECH_DISCOVERED類型的Intent對象 |
RTD_HANDOVER_CARRIER |
退化到ACTION_TECH_DISCOVERED類型的Intent對象 |
RTD_HANDOVER_REQUEST |
退化到ACTION_TECH_DISCOVERED類型的Intent對象 |
RTD_HANDOVER_SELECT |
退化到ACTION_TECH_DISCOVERED類型的Intent對象 |
RTD_SMART_POSTER |
基於負載解析的URI |
RTD_TEXT |
text/plain類型的MIME |
RTD_URI |
基於有效負載的URI |
應用程序如何調度NFC標籤
當標籤調度系統完成對NFC標籤和它的標識信息封裝的Intent對象的建立時,它會把該Intent對象發送給感興趣的應用程序。若是有多個應用程序可以處理該Intent對象,就會顯示Activity選擇器,讓用戶選擇Activity。標籤調度系統定義了三種Intent對象,如下按照由高到低的優先級列出這三種Intent對象:
1. ACTION_NDEF_DISCOVERED:這種Intent用於啓動包含NDEF負載和已知類型的標籤的Activity。這是最高優先級的Intent,而且標籤調度系統在任何其餘Intent以前,都會盡量的嘗試使用這種類型的Intent來啓動Activity。
2. ACTION_TECH_DISCOVERED:若是沒有註冊處理ACTION_NDEF_DISCOVERED類型的Intent的Activity,那麼標籤調度系統會嘗試使用這種類型的Intent來啓動應用程序。若是被掃描到的標籤包含了不能被映射到MIME類型或URI的NDEF數據,或者沒有包含NDEF數據,可是是已知的標籤技術,那麼也會直接啓動這種類型的Intent對象(而不是先啓動ACTION_NDEF_DISCOVERED類型的Intent)
3. ACTION_TAB_DISCOVERED:若是沒有處理ACTION_NDEF_DISCOVERED或ACTION_TECH_DISCOVERED類型Intent的Activity,就會啓動這種類型的Intent。
標籤調度系統的基本工做方法以下:
1. 用解析NFC標籤時由標籤調度系統建立的Intent對象(ACTION_NDEF_DISCOVERED或ACTION_TECH_DISCOVERED)來嘗試啓動Activity;
2. 若是沒有對應的處理Intent的Activity,那麼就會嘗試使用下一個優先級的Intent(ACTION_TECH_DISCOVERED或ACTION_TAG_DISCOVERED)來啓動Activity,直到有對應的應用程序來處理這個Intent,或者是直到標籤調度系統嘗試了全部可能的Intent。
3. 若是沒有應用程序來處理任何類型的Intent,那麼就不作任何事情。
![](http://static.javashuo.com/static/loading.gif)
圖1.標籤調度系統
在可能的狀況下,都會使用NDEF消息和ACTION_NDEF_DISCOVERED類型的Intent來工做,由於它是這三種Intent中最標準的。這種Intent與其餘兩種Intent相比,它會容許你在更加合適的時機來啓動你的應用程序,從而給用戶帶來更好的體驗。
在Android的Manifest中申請NFC訪問
在訪問設備的NFC硬件和正確的處理NFC的Intent以前,要在AndroidManifest.xml文件中進行如下聲明:
1. 在<uses-permission>元素中聲明訪問NFC硬件:
<uses-permission android:name="android.permission.NFC" />
2. 你的應用程序所支持的最小的SDK版本。API Level 9只經過ACTION_TAG_DISCOVERED來支持有限的標籤調度,而且只能經過EXTRA_NDEF_MESSAGES來訪問NDEF消息。沒有其餘的標籤屬性或I/O操做可用。API Level 10中包含了普遍的讀寫支持,從而更好的推進了NDEF的應用前景,而且API Leve 14用Android Beam和額外的方便的建立NDEF記錄的方法,向外提供了更容易的把NDEF消息推送給其餘設備的方法。
<uses-sdkandroid:minSdkVersion="10"/>
3. 使用uses-feature元素,在Google Play中,以便你的應用程序可以只針對有NFC硬件的設備來顯示。
<uses-featureandroid:name="android.hardware.nfc"android:required="true"/>
若是你的應用程序使用了NFC功能,可是相關的功能又不是你的應用程序的關鍵功能,你能夠忽略uses-feature元素,而且要在運行時經過調用getDefaultAdapter()方法來檢查NFC是否有效。
過濾NFC的Intent
要在你想要處理被掃描到的NFC標籤時啓動你的應用程序,能夠在你的應用程序的Android清單中針對一種、兩種或所有三種類型的NFC的Intent來過濾。可是,一般想要在應用程序啓動時控制最經常使用的ACTION_NDEF_DISCOVERED類型的Intent。在沒有過濾ACTION_NDEF_DISCOVERED類型的Intent的應用程序,或數據負載不是NDEF時,纔會從ACTION_NDEF_DISCOVERED類型的Intent回退到ACTION_TECH_DISCOVERED類型的Intent。一般ACTION_TAB_DISCOVERED是最通常化的過濾分類。不少應用程序都會在過濾ACTION_TAG_DISCOVERED以前,過濾ACTION_NDEF_DISCOVERED或ACTION_TECH_DISCOVERED,這樣就會下降你的應用程序被啓動的可能性。ACTION_TAG_DISCOVERED只是在沒有應用程序處理ACTION_NDEF_DISCOVERED或ACTION_TECH_DISCOVERED類型的Intent的狀況下,才使用的最後手段。
由於NFC標籤的多樣性,而且不少時候不在你的控制之下,所以在必要的時候你要回退到其餘兩種類型的Intent。在你可以控制標籤的類型和寫入的數據時,咱們建議你使用NDEF格式。下文將介紹如何過濾每種類型的Intent對象。
ACTION_NDEF_DISCOVERED
要過濾ACTION_NDEF_DISCOVERED類型的Intent,就要在清單中跟你想要過濾的數據一塊兒來聲明該類型的Intent過濾器。如下是過濾text/plain類型的MIME的ACTION_NDEF_DISCOVERED類型過濾器的聲明:
<intent-filter>
<action android:name="android.nfc.action.NDEF_DISCOVERED"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:mimeType="text/plain" />
</intent-filter>
如下示例使用http://developer.android.com/index.html格式的URI來過濾:
<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
若是你的Activity要過濾ACTION_TECH_DISCOVERED類型的Intent,你必須建立一個XML資源文件,該文件在tech-list集合中指定你的Activity所支持的技術。若是tech-list集合是標籤所支持的技術的一個子集,那麼你的Activity被認爲是匹配的。經過調用getTechList()方法來得到標籤所支持的技術集合。
例如,若是掃描到的標籤支持MifareClassic、NdefFormatable和NfcA,那麼爲了跟它們匹配,tech-list集合就必須指定全部這三種技術,或者指定其中的兩種或一種。
如下示例定義了全部的相關的技術。你能夠根據須要刪除其中一些設置。而後把這個文件保存到<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()返回的技術的子集,那麼你的Activity就被認爲是匹配的。下列示例可以跟支持NfcA和Ndef技術NFC標籤或者跟支持NfcB和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_TAG_DISCOVERED
使用下列Intent過濾器來過濾ACTION_TAG_DISCOVERED類型的Intent:
<intent-filter>
<action android:name="android.nfc.action.TAG_DISCOVERED"/>
</intent-filter>
從Intent中獲取信息
若是由於NFC的Intent而啓動一個Activity,那麼你就可以從Intent中獲取被掃描到的NFC標籤的相關信息。根據被掃描到的標籤,Intent對象可以如下額外的信息:
1. EXTRA_TAG(必須的):它是一個表明了被掃描到的標籤的Tag對象;
2. EXTRA_NDEF_MESSAGES(可選):它是一個解析來自標籤中的NDEF消息的數組。這個附加信息是強制在Intent對象上的;
3. {@link android.nfc.NfcAdapter#EXTRA_ID(可選):標籤的低級ID。
要獲取這些附加信息,就要確保你的Activity是被掃描到的NFC的Intent對象啓動的,而後才能得到Intent以外的附加信息。下例檢查ACTION_NDEF_DISCOVERED類型的Intent,並從Intent對象的附加信息中獲取NDEF消息。
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 Level14)開始,能夠用createUri()方法來幫助你自動的建立URI記錄。從Android4.1(API Level 16)開始,能夠用createExternal()和createMime()方法來幫助你建立MIME和外部類型的NDEF記錄。使用這些輔助方法會盡量的避免手動建立NDEF記錄的錯誤。
本節還要介紹如何建立NDEF記錄對應的Intent過濾器。全部的這些寫入或發送到NFC標籤的NDEF記錄例子都應該是NDEF消息的第一條記錄。
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]);
對應的Intent過濾器以下:
<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")));
對應的Intent過濾器以下:
<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和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;
}
對應的Intent過濾器以下:
<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_KNOW和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);
對應的Intent過濾器以下:
<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類型的記錄。
使用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);
對應的Intent過濾器以下:
<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類型NFC部署,以便更好的支持Android設備和非Android設備。
注意:TNF_EXTERNAL_TYPE類型的URN包含如下格式:
urn:nfc:ext:example.com.externalType,可是,NFC論壇的RTD規範聲明,URN的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應用程序記錄(Android Application Record---AAR)
在Android4.0(API Level 14)中引入的Android應用程序記錄(AAR),提供了較強的在掃描到NFC標籤時,啓動應用程序的肯定性。AAR有嵌入到NDEF記錄內部的應用程序的包名。你可以把一個AAR添加到你的NDEF消息的任何記錄中,由於Android會針對AAR來搜索整個NDEF消息。若是它找到一個AAR,它就會基於AAR內部的包名來啓動應用程序。若是該應用程序不在當前的設備上,會啓動Google Play來下載對應的應用程序。
若是你想要防止其餘的應用對相同的Intent的過濾並潛在的處理你部署的特定的NFC標籤,那麼AAR是有用的。AAR僅在應用程序級被支持,由於包名的約束,並不能在Activity級別來過濾Intent。若是你想要在Activity級處理Intent,請使用Intent過濾器。
若是NFC標籤中包含了AAR,則NFC標籤調度系統會按照下列方式來調度:
1. 一般,嘗試使用Intent過濾器來啓動一個Activity。若是跟該Intent匹配的Activity也跟AAR匹配,那麼就啓動該Activity。
2. 若是跟Intent隊形的Activity跟AAR不匹配,或者是有多個Activity可以處理該Intent,或者是沒有可以處理該Intent的Activity存在,那麼就啓動由AAR指定的應用程序。
3. 若是沒有跟該AAR對應的應用程序,那麼就會啓動Google Play來小組基於該AAR的應用程序。
注意:你可以用前臺調度系統來重寫AAR和Intent調度系統,在NFC標籤被發現時。它容許優先使用前臺的Activity。用這種方法,Activity必須是在前臺來重寫AAR和Intent調度系統。
若是你依然想要過濾掃描到的沒有包含AAR的NFC標籤,一般,你可以聲明Intent過濾器。若是你的應用程序對不包含AAR的其餘NFC標籤感興趣,這種作法是有用的。例如,你可能想要保證你的應用程序處理你部署的專用NFC標籤,以及由第三方部署的普通的NFC標籤。要記住AAR是在Android4.0之後才指定的,所以部署NFC標籤時,你極可能但願使用可以普遍支持AAR和MIME類型/URI的是設備。另外,在你部署NFC標籤時,還要想如何編寫你的NFC標籤,以便讓大多數設備(Android設備和其餘設備)支持。同過定義相對惟一的MIME類型或URI,讓應用程序更容易的區分,就能夠作到這一點。
Android提供了簡單的建立AAR的API:createApplicationRecord()。你須要作的全部工做就是把AAR嵌入到你的NdefMessage中。除非AAR是NdefMessage中的惟一記錄,不然不要把使用NdefMessage的第一條記錄。這是由於,Android系統會檢查NdefMessage的第一條記錄來判斷NFC標籤的MIME類型或URI,這些信息被用於建立對應應用程序的Intent對象。如下代碼演示瞭如何建立一個AAR:
NdefMessage msg = new NdefMessage(
new NdefRecord[] {
...,
NdefRecord.createApplicationRecord("com.example.android.beam")}
把NDEF消息發射到其餘設備上
Android Beam容許在兩個Android設備之間進行簡單的對等數據交換,想要把數據發送給另外一個設備的應用程序必須是在前臺,而且接收數據的設備必須不被鎖定。當發射設備跟接收設備的距離足夠近的時候,發射設備會顯示「Touch to Beam(觸摸發射)」的UI。而後,用戶可以選擇是否把消息發射給接收設備。
注意:在API Level 10中能夠利用前臺的NDEF推送,它提供了與Android Beam相似的功能。這些API已通過時了,可是在一些老舊設備上還有效。更多的信息請看enableForegroundNdefPush()。
經過調用下列兩個方法中的任意一個,就可以爲你的應用程序啓用Android Beam:
1. setNdefPushMessage():這個方法把接收到的NdefMessage對象做爲一個消息設置給Beam。當兩個設備足夠近的時候,就會自動的發送消息。
2. setNdefPushMessageCallback():接收包含createNdefMessage()方法的回調,當設備在發射數據的範圍內時,這個回調方法會被調用。回調會讓你只在須要的時候建立NDEF消息。
一個Activity一次只能推送一條NDEF消息,所以若是同時使用了這兩種方法,那麼setNdefPushMessageCallback()方法的優先級要高於setNdefPushMessage()方法。要使用Android Beam,一般必須知足如下條件:
1. 發射數據的Activity必須是在前臺。兩個設備的屏幕都必須沒有被鎖定;
2. 必須發要發射的數據封裝到一個NdefMessage對象中;
3. 接收發射數據的NFC設備必須支持com.android.npp NDEF推送協議或是NFC組織的SNEP協議(簡單的NDEF交換協議)。在API Level9(Android2.3)到API Level 13(Android3.2)的設備上須要com.android.npp協議。在API Level 14(Android4.0)和之後的設備上,com.android.npp和SNEP都須要。
注意:若是在前臺的Activity啓用了Android Beam,那麼標準的Intent調度系統就會被禁用。可是,若是該Activity還啓用了前臺調度,那麼在前臺調度系統中,它依然可以掃描到跟Intent過濾器匹配的NFC標籤。
啓用Android Beam:
1. 建立一個準備推送到另外一個設備上的包含NdefRecord的NdefMessage對象。
2. 調用帶有NdefMessage類型參數的setNdefPushMessage()方法,或者是在Activity的onCreate()方法中調用setNdefPushMessageCallback方法來傳遞實現NfcAdapter.CreateNdefMessageCallback接口的對象。這兩個方法都至少須要一個準備要啓用Android Beam的Activity,以及一個可選的其餘的活躍的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!\n\n" +
"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就會啓動下載程序。所以,若是使用AAR,對於Android4.0之後的設備,下列的Intent過濾器,在技術上不是必須的:
<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>
有了這個Intent過濾器,com.example.android.beam應用就可以在如下狀況下被啓動:
1. 掃描到NFC標籤;
2. 接收到com.example.android.beam類型的AAR或NDEF消息中包含一條application/vnd.com.example.android.beam類型的MIME記錄的Android beam的時候。
即便經過AAR可以保證了一個應用程序被啓動或下載,可是仍是推薦使用Intent過濾器,由於它會讓你選擇啓動應用程序中Activity,而不是總啓動AAR中指定的應用程序包的主Activity。AAR沒有Activity級別的粒度。並且還有一些android設備不支持AAR,你還應該在NDEF消息的第一條NDEF記錄中嵌入標識信息,以及對應的過濾器。