Android近場通訊---NFC基礎轉)

Android近場通訊---NFC基礎(一)(轉)

本文介紹在Android系經過你所能執行的基本任務。它解釋瞭如何用NDEF消息格式來發送和接收NFC數據,而且介紹了支持這些功能的Android框架API。有關更高級的話題,包括對非NDEF格式數據的討論,狀況「高級 NFChtml

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

 

 
 

Android近場通訊---NFC基礎(二)(轉)

應用程序如何調度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,那麼就不作任何事情。

圖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是否有效。

 

Android近場通訊---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>

 

Android近場通訊---NFC基礎(四)(轉)

從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近場通訊---NFC基礎(五)(轉)

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記錄中嵌入標識信息,以及對應的過濾器。

相關文章
相關標籤/搜索