NFC 基本概念

本文件說明您在 Android 上執行的基本 NFC 工作。另外還會說明如何傳送 以 NDEF 訊息的形式接收 NFC 資料,並說明支援哪個 Android 架構 API 這些功能。此外,還探討瞭如何使用非 NDEF 資料等進階主題 請參閱進階 NFC

使用 NDEF 資料和 Android 時有兩種主要用途:

透過標記分派方式讀取 NFC 標記的 NDEF 資料 系統,可分析找到的 NFC 標記,將資料適當分類並啟動 對分類資料感興趣的應用程式。想要處理 掃描的 NFC 標記可以宣告意圖篩選器 處理資料的要求。

Android BeamTM 功能可讓裝置將 NDEF 訊息推送至 以實際方式觸碰到其他裝置。透過這種互動 能夠以藍牙等其他無線技術傳送資料,因為這類技術需搭配 NFC 不需要手動操作裝置 必須提供探索或配對連線功能。連上兩部裝置後,系統就會自動啟動連線 移至範圍內Android Beam 可透過一組 NFC API 使用,因此任何應用程式皆可 提供個人專屬資訊例如,聯絡人、瀏覽器和 YouTube 應用程式會使用 Android Beam 可與其他裝置分享聯絡人、網頁和影片。

代碼分派系統

螢幕時,Android 裝置通常會尋找 NFC 標記 除非已在裝置的「設定」選單中停用 NFC,否則保持解鎖狀態。 當 Android 裝置發現 NFC 標記時,預期的行為 就是採用最合適的活動處理意圖,不必詢問使用者哪個應用程式 。由於裝置掃描的 NFC 標記範圍極短,因此使用者必須手動完成 選取一個活動,會強制使裝置遠離標記,並中斷連線。 您應該開發活動,只處理活動關注的 NFC 標記 防止出現活動選擇工具。

為協助您達成這個目標,Android 提供了特殊的標記分派系統,用來分析掃描結果 NFC 標記會剖析這些標記,並嘗試找出對掃描資料感興趣的應用程式。這項服務 方法如下:

  1. 剖析 NFC 標記並解讀 MIME 類型或可識別資料酬載的 URI 。
  2. 將 MIME 類型或 URI 及酬載封裝至意圖。前兩個 詳細步驟請參閱將 NFC 標記對應至 MIME 類型和 URI
  3. 根據意圖啟動活動。本程式碼的 如何將 NFC 標記分派給應用程式

NFC 標記如何對應至 MIME 類型和 URI

開始編寫 NFC 應用程式之前,請務必先瞭解 NFC 標記類型、標記分派系統如何剖析 NFC 標記,以及該標記的特殊工作 派出系統偵測到 NDEF 訊息內建 NFC 標記 多種技術,還能以不同的方式寫入資料。 Android 為 NDEF 標準提供最多支援,該標準是由 NFC 論壇定義。

NDEF 資料會封裝在內含訊息的訊息 (NdefMessage) 中 或多筆記錄 (NdefRecord)。每筆 NDEF 記錄都必須根據 您要建立的記錄類型規格。Android 版 也支援其他不含 NDEF 資料的代碼類型; android.nfc.tech 套件中的類別。瞭解詳情 如要瞭解這些技術,請參閱進階 NFC 主題。使用上述其他類型的代碼時 您可以自行撰寫通訊協定堆疊來與代碼通訊,因此,建議您在下列時機使用 NDEF 以便輕鬆開發及獲得最多的 Android 裝置支援。

注意: 如要下載完整的 NDEF 規格,請參閱 NFC 論壇規格與應用程式文件網站,並查看 建立常見的 NDEF 記錄類型範例,瞭解如何 建構 NDEF 記錄

您已瞭解 NFC 標記的背景資料,以下章節會詳細說明 Android 會處理 NDEF 格式的標記。Android 裝置掃描含有 NDEF 的 NFC 標記時 格式化資料後,它會剖析訊息,並嘗試得出資料的 MIME 類型, URI。為此,系統會讀取 NdefMessage 中的第一個 NdefRecord,以決定如何解讀整個 NDEF 訊息 (NDEF 訊息可以 以及多筆 NDEF 記錄)。在格式正確的 NDEF 訊息中,第一個 NdefRecord 包含下列欄位:

3 位元 TNF (類型名稱格式)
指出如何解讀變數長度類型欄位。有效值為 如表 1 所述。
變數長度類型
說明記錄的類型,如果使用 TNF_WELL_KNOWN,請使用 這個欄位來指定記錄類型定義 (RTD)。如需有效 RTD 值的說明,請參閱表 2
變數長度 ID
記錄的專屬 ID。這個欄位不常使用,但 如果需要專門識別某個代碼,可以為其建立 ID。
變數長度酬載
您要讀取或寫入的實際資料酬載。NDEF 訊息可以包含多個 NDEF 記錄,因此請不要假設完整承載位於第一個 NDEF NDEF 訊息的紀錄。

標記分派系統會使用 TNF 和類型欄位,嘗試將 MIME 類型或 URI 對應至 NDEF 訊息。如果成功,就會封裝 ACTION_NDEF_DISCOVERED 意圖中的資訊及實際酬載。不過, 在代碼分派系統無法根據第一個 NDEF 判斷資料類型時, 。當 NDEF 資料無法對應至 MIME 類型或 URI,或是 NFC 標記不含開頭的 NDEF 資料。在這種情況下,含有代碼技術和酬載的 Tag 物件 改為封裝在 ACTION_TECH_DISCOVERED 意圖中。

表 1 說明標記分派系統如何對應 TNF 和類型 等欄位對應至 MIME 類型或 URI同時說明哪些 TNF 無法對應至 MIME 類型或 URI。 在這類情況下,代碼分派系統會改回使用 ACTION_TECH_DISCOVERED

舉例來說,如果標記分派系統遇到 TNF_ABSOLUTE_URI 類型的記錄,就會對應該記錄的變數長度類型欄位 轉換為 URI標記分派系統會將該 URI 封裝在 ACTION_NDEF_DISCOVERED 意圖的資料欄位中,以及標記的其他資訊。 例如酬載另一方面,如果遇到 TNF_UNKNOWN 類型的記錄,就會建立封裝代碼技術的意圖 。

表 1. 支援的 TNF 及其對應

類型名稱格式 (TNF) 對應
TNF_ABSOLUTE_URI URI。
TNF_EMPTY 改回 ACTION_TECH_DISCOVERED
TNF_EXTERNAL_TYPE URI (以類型欄位中的 URN 為基礎)。URN 會編碼為 以下為短格式:<domain_name>:<service_name>。 Android 會將此對應至 URI,格式如下: 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 (取決於您在 類型。詳情請參閱表 2 可用的 RTD 及其對應項目。

表 2. 系統支援的 TNF_WELL_KNOWN 及其 RTD 對應

記錄類型定義 (RTD) 對應
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 text/plain 的 MIME 類型。
RTD_URI 根據酬載產生的 URI

NFC 標記如何分派給應用程式

標記分派系統建立完成後,將 NFC 標記及其 會將意圖傳送至 以便套用意圖篩選器如果有多個應用程式可以處理意圖,活動選擇工具 方便使用者選取活動。標記分派系統會定義三個意圖 按優先順序由高到低列出:

  1. ACTION_NDEF_DISCOVERED:這項意圖的用途是啟動 系統在掃描包含 NDEF 酬載的標記類型為可辨識的標記時的活動。這是 而標記分派系統會嘗試以這個方式啟動活動 產生了其他意圖
  2. ACTION_TECH_DISCOVERED:如果沒有任何活動註冊至 處理 ACTION_NDEF_DISCOVERED 意圖,標記分派系統會嘗試以此意圖啟動應用程式。這個 如果已掃描標記,意圖也會直接啟動 (不必先從 ACTION_NDEF_DISCOVERED 開始) 包含無法對應到 MIME 類型或 URI 的 NDEF 資料,或標記未包含 NDEF 並且採用已知的標記技術
  3. ACTION_TAG_DISCOVERED:此意圖已啟動 如果沒有活動處理 ACTION_NDEF_DISCOVEREDACTION_TECH_DISCOVERED 意圖。

代碼分派系統的基本運作方式如下:

  1. 嘗試使用標記分派系統建立的意圖來啟動活動 剖析 NFC 標記時 ( ACTION_NDEF_DISCOVEREDACTION_TECH_DISCOVERED)。
  2. 如果該意圖沒有任何活動篩選器,請嘗試透過後續動作啟動活動 最低優先意圖 (ACTION_TECH_DISCOVEREDACTION_TAG_DISCOVERED),直到針對 或直到標記分派系統嘗試所有可能的意圖為止。
  3. 如果沒有任何應用程式篩選任何意圖,則不執行任何動作。
,瞭解如何調查及移除這項存取權。
圖 1.標記調度系統

請盡可能使用 NDEF 訊息和 ACTION_NDEF_DISCOVERED 意圖,因為這是 這三點此意圖使您在更合適的時間啟動應用程式 另外兩個意圖,為使用者提供更優質的體驗。

在 Android 資訊清單中要求 NFC 存取權

如要存取裝置的 NFC 硬體,並正確處理 NFC 意圖,請先宣告這些 AndroidManifest.xml 檔案中的項目:

  • 可存取 NFC 硬體的 NFC <uses-permission> 元素:
    <uses-permission android:name="android.permission.NFC" />
    
  • 應用程式支援的最低 SDK 版本。API 級別 9 僅支援 僅透過 ACTION_TAG_DISCOVERED 傳送有限的標記 透過 EXTRA_NDEF_MESSAGES 額外存取 NDEF 訊息。否 其他代碼屬性或 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()null

NFC 意圖的篩選器

如要在掃描到您想處理的 NFC 標記時啟動應用程式,您的應用程式 可以在 Android 資訊清單中,篩選一個、兩個或全部三個 NFC 意圖。不過, 您通常會想要篩選 ACTION_NDEF_DISCOVERED 意圖 大部分控管應用程式啟動的時間如果沒有應用程式篩選項目,ACTION_TECH_DISCOVERED 意圖就是 ACTION_NDEF_DISCOVERED 的備用方案 ACTION_NDEF_DISCOVERED 或酬載不是酬載時 NDEF。篩選 ACTION_TAG_DISCOVERED 通常太過籠統, 要做為篩選依據的類別許多應用程式都會在 ACTION_TAG_DISCOVERED 之前篩選 ACTION_NDEF_DISCOVEREDACTION_TECH_DISCOVERED,因此您的應用程式 。ACTION_TAG_DISCOVERED 只能在不得已的情況下使用 讓應用程式在沒有安裝以處理 ACTION_NDEF_DISCOVEREDACTION_TECH_DISCOVERED 意圖。

由於 NFC 標記部署項目不盡相同,而且往往並非由您控管,因此不一定每次都會顯示 因此,您可以視需要改用另外兩個意圖。當您 控制代碼與寫入資料的類型,建議您使用 NDEF 來設定 標記內。以下各節說明如何篩選各種意圖類型。

ACTION_NDEF_DISCOVERED

如要篩選 ACTION_NDEF_DISCOVERED 意圖,請宣告 意圖篩選器及要篩選的資料類型。 下列篩選器範例:ACTION_NDEF_DISCOVERED 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>

下列範例會透過 https://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="https"
              android:host="developer.android.com"
              android:pathPrefix="/index.html" />
</intent-filter>

ACTION_TECH_DISCOVERED

如果您的活動篩選器用於 ACTION_TECH_DISCOVERED 意圖, 您必須建立 XML 資源檔案,指定活動支援的技術 在 tech-list 集合內。您的活動是 如果 tech-list 組合屬於 支援此功能,只要呼叫 getTechList() 即可取得。

舉例來說,如果掃描的標記支援 Mifare Classic、NdefFormatable 和 NfcA,那麼您的 tech-list 集必須指定全部、兩項或其中一種技術 (且不得指定) ),才能比對您的活動。

以下範例定義了所有技術。您必須移除無關的目標對象 可支援 NFC 標記。請將這個檔案儲存在 <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() 傳回的技術子集。這種做法可讓ANDOR 比對技術的語意。以下範例比對支援 NfcA 和 Ndef 技術,或支援 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>
    <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 意圖,請參閱使用支援的代碼 進階 NFC 文件中的技術

ACTION_TAG_探索

如要篩選 ACTION_TAG_DISCOVERED,請使用下列意圖 篩選器:

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

從意圖取得資訊

如果活動是因為 NFC 意圖而啟動,您可以取得掃描 NFC 的相關資訊 標記在意圖中根據掃描的標記,意圖可包含以下額外項目:

為了取得這些額外項目,請檢查活動是否已使用其中一個版本啟動 NFC 意圖,藉此確保該標記已掃描完成 意圖。下列範例會檢查 ACTION_NDEF_DISCOVERED 並從意圖額外項目中取得 NDEF 訊息

Kotlin

override fun onNewIntent(intent: Intent) {
    super.onNewIntent(intent)
    ...
    if (NfcAdapter.ACTION_NDEF_DISCOVERED == intent.action) {
        intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES)?.also { rawMessages ->
            val messages: List<NdefMessage> = rawMessages.map { it as NdefMessage }
            // Process the messages array.
            ...
        }
    }
}

Java

@Override
protected void onNewIntent(Intent intent) {
    super.onNewIntent(intent);
    ...
    if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(intent.getAction())) {
        Parcelable[] rawMessages =
            intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);
        if (rawMessages != null) {
            NdefMessage[] messages = new NdefMessage[rawMessages.length];
            for (int i = 0; i < rawMessages.length; i++) {
                messages[i] = (NdefMessage) rawMessages[i];
            }
            // Process the messages array.
            ...
        }
    }
}

或者,您也可以從意圖取得 Tag 物件, 包含酬載,並可讓您列舉標記的技術:

Kotlin

val tag: Tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG)

Java

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

建立常見的 NDEF 記錄類型

本節說明如何建立常見的 NDEF 記錄類型,協助您寫入以下項目 NFC 標記或透過 Android Beam 傳送資料。自 Android 4.0 (API 級別 14) 起, 您可以使用 createUri() 方法建立 URI 會自動記錄。自 Android 4.1 (API 級別 16) 起, createExternal()createMime() MIME 和外部類型的 NDEF 記錄。請盡可能使用這些輔助方法,避免出錯 必須能手動建立 NDEF 記錄

本節也會說明如何建立對應的 意圖篩選器,以擷取記錄。所有 NDEF 記錄範例均應位於前 NDEF 中 紀錄。

TNF_ABSOLUTE_URI

注意:建議您使用 請改為輸入 RTD_URI 類型 (TNF_ABSOLUTE_URI),因為這樣效率更高。

您可以按照下列方式建立 TNF_ABSOLUTE_URI NDEF 記錄 :

Kotlin

val uriRecord = ByteArray(0).let { emptyByteArray ->
    NdefRecord(
            TNF_ABSOLUTE_URI,
            "https://developer.android.com/index.html".toByteArray(Charset.forName("US-ASCII")),
            emptyByteArray,
            emptyByteArray
    )
}

Java

NdefRecord uriRecord = new NdefRecord(
    NdefRecord.TNF_ABSOLUTE_URI ,
    "https://developer.android.com/index.html".getBytes(Charset.forName("US-ASCII")),
    new byte[0], new byte[0]);

先前的 NDEF 記錄的意圖篩選器如下所示:

<intent-filter>
    <action android:name="android.nfc.action.NDEF_DISCOVERED" />
    <category android:name="android.intent.category.DEFAULT" />
    <data android:scheme="https"
        android:host="developer.android.com"
        android:pathPrefix="/index.html" />
</intent-filter>

TNF_MIME_MEDIA

您可以按照下列方式建立 TNF_MIME_MEDIA NDEF 記錄:

使用 createMime() 方法:

Kotlin

val mimeRecord = NdefRecord.createMime(
        "application/vnd.com.example.android.beam",
        "Beam me up, Android".toByteArray(Charset.forName("US-ASCII"))
)

Java

NdefRecord mimeRecord = NdefRecord.createMime("application/vnd.com.example.android.beam",
    "Beam me up, Android".getBytes(Charset.forName("US-ASCII")));

手動建立 NdefRecord

Kotlin

val mimeRecord = Charset.forName("US-ASCII").let { usAscii ->
    NdefRecord(
            NdefRecord.TNF_MIME_MEDIA,
            "application/vnd.com.example.android.beam".toByteArray(usAscii),
            ByteArray(0),
            "Beam me up, Android!".toByteArray(usAscii)
    )
}

Java

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>
    <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 記錄:

Kotlin

fun createTextRecord(payload: String, locale: Locale, encodeInUtf8: Boolean): NdefRecord {
    val langBytes = locale.language.toByteArray(Charset.forName("US-ASCII"))
    val utfEncoding = if (encodeInUtf8) Charset.forName("UTF-8") else Charset.forName("UTF-16")
    val textBytes = payload.toByteArray(utfEncoding)
    val utfBit: Int = if (encodeInUtf8) 0 else 1 shl 7
    val status = (utfBit + langBytes.size).toChar()
    val data = ByteArray(1 + langBytes.size + textBytes.size)
    data[0] = status.toByte()
    System.arraycopy(langBytes, 0, data, 1, langBytes.size)
    System.arraycopy(textBytes, 0, data, 1 + langBytes.size, textBytes.size)
    return NdefRecord(NdefRecord.TNF_WELL_KNOWN, NdefRecord.RTD_TEXT, ByteArray(0), data)
}

Java

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>
    <action android:name="android.nfc.action.NDEF_DISCOVERED" />
    <category android:name="android.intent.category.DEFAULT" />
    <data android:mimeType="text/plain" />
</intent-filter>

使用 RTD_URI 的 TNF_WELL_KNOWN

您可以按照下列方式建立 TNF_WELL_KNOWN NDEF 記錄:

使用 createUri(String) 方法:

Kotlin

val rtdUriRecord1 = NdefRecord.createUri("https://example.com")

Java

NdefRecord rtdUriRecord1 = NdefRecord.createUri("https://example.com");

使用 createUri(Uri) 方法:

Kotlin

val rtdUriRecord2 = Uri.parse("https://example.com").let { uri ->
    NdefRecord.createUri(uri)
}

Java

Uri uri = Uri.parse("https://example.com");
NdefRecord rtdUriRecord2 = NdefRecord.createUri(uri);

手動建立 NdefRecord

Kotlin

val uriField = "example.com".toByteArray(Charset.forName("US-ASCII"))
val payload = ByteArray(uriField.size + 1)                   //add 1 for the URI Prefix
payload [0] = 0x01                                           //prefixes https://www. to the URI
System.arraycopy(uriField, 0, payload, 1, uriField.size)     //appends URI to payload
val rtdUriRecord = NdefRecord(NdefRecord.TNF_WELL_KNOWN, NdefRecord.RTD_URI, ByteArray(0), payload)

Java

byte[] uriField = "example.com".getBytes(Charset.forName("US-ASCII"));
byte[] payload = new byte[uriField.length + 1];              //add 1 for the URI Prefix
payload[0] = 0x01;                                           //prefixes https://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>
    <action android:name="android.nfc.action.NDEF_DISCOVERED" />
    <category android:name="android.intent.category.DEFAULT" />
    <data android:scheme="https"
        android:host="example.com"
        android:pathPrefix="" />
</intent-filter>

TNF 外部類型

您可以在下列位置建立 TNF_EXTERNAL_TYPE NDEF 記錄 方式:

使用 createExternal() 方法:

Kotlin

var payload: ByteArray //assign to your data
val domain = "com.example" //usually your app's package name
val type = "externalType"
val extRecord = NdefRecord.createExternal(domain, type, payload)

Java

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

Kotlin

var payload: ByteArray
...
val extRecord = NdefRecord(
        NdefRecord.TNF_EXTERNAL_TYPE,
        "com.example:externalType".toByteArray(Charset.forName("US-ASCII")),
        ByteArray(0),
        payload
)

Java

byte[] payload;
...
NdefRecord extRecord = new NdefRecord(
    NdefRecord.TNF_EXTERNAL_TYPE, "com.example:externalType".getBytes(Charset.forName("US-ASCII")),
    new byte[0], payload);

先前的 NDEF 記錄的意圖篩選器如下所示:

<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: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,也就是 範例的意圖篩選器會宣告

Android 應用程式記錄

已在 Android 4.0 (API 級別 14) 中推出,Android 應用程式記錄 (AAR) 可提供更強大的功能 確認您的應用程式會在掃描 NFC 標記時啟動。AAR 包含套件名稱 內嵌於 NDEF 記錄中的應用程式您可以在 NDEF 的任何 NDEF 記錄中新增 AAR 因為 Android 會搜尋完整的自動套用建議功能 NDEF 訊息。如果找到自動套用最佳化建議,系統會啟動 依據 AAR 中的套件名稱建立應用程式如果應用程式 裝置時,啟動 Google Play 即可下載這個應用程式。

如果不想讓其他應用程式篩選相同意圖,不妨使用「自動套用最佳化建議」功能 也可能會處理您已部署的特定標記「自動套用最佳化建議」功能僅適用於 應用程式層級,因為套件名稱限製而無法在活動層級 (如 意圖篩選功能。如果您想在活動層級處理意圖,請使用意圖篩選器

如果代碼包含 AAR,代碼分派系統會以下列方式發送:

  1. 嘗試照常使用意圖篩選器啟動 Activity。如果相符的活動 意圖也與 AAR 相符,請啟動 Activity。
  2. 如果篩選意圖的活動與 AAR、如果多個活動可以處理意圖,或者沒有活動處理意圖,請啟動 指定的 AAR 擴充應用程式
  3. 如果所有應用程式都無法以「自動套用最佳化建議」功能啟動,請前往 Google Play 下載 實作 AAR 的應用程式

注意:您可以使用下列程式碼,覆寫 AAR 和意圖調度系統: 前景 調度系統,可在 NFC 標記 發現。使用這個方法時,活動必須在前景執行才能覆寫 AAR,且 意圖調度系統

如果您仍想篩選不含 AAR 的掃描代碼,您可以宣告 新增意圖篩選器如果您的應用程式對其他標記感興趣,這項功能就非常實用 但不含「自動套用最佳化建議」功能的物件例如,您可能希望確保應用程式 專屬標記,以及第三方部署的一般標記。注意事項 「自動套用最佳化建議」功能只適用於 Android 4.0 以上版本的裝置,因此在部署代碼時 ,使用 AAR 和 MIME 類型/URI,以支援最廣泛的裝置。於 此外,當您部署 NFC 標記時,請考慮如何編寫 NFC 標記以啟用 支援大多數的裝置 (Android 及其他裝置)。方法如下: 定義相對獨特的 MIME 類型或 URI,方便應用程式區別。

Android 提供簡單的 API 來建立 AAR createApplicationRecord()。隨心所欲 確實將 AAR 嵌入 NdefMessage 中的任何位置。您不想要 使用 NdefMessage 的第一筆記錄,除非 AAR 是唯一的 記錄 (在 NdefMessage 中)。這是因為 Android Studio 系統會檢查 NdefMessage 的第一個記錄來判斷 MIME 類型,或 標記的 URI,用來建立應用程式篩選的意圖。以下程式碼 以下說明如何建立自動套用最佳化建議:

Kotlin

val msg = NdefMessage(
        arrayOf(
                ...,
                NdefRecord.createApplicationRecord("com.example.android.beam")
        )
)

Java

NdefMessage msg = new NdefMessage(
        new NdefRecord[] {
            ...,
            NdefRecord.createApplicationRecord("com.example.android.beam")}
        );
)

將 NDEF 訊息傳輸至其他裝置

Android Beam 可讓您在兩台 Android 裝置之間進行簡單的點對點資料交換。 如果應用程式需要將資料傳輸至其他裝置,就必須在前景執行 不得鎖定接收資料當警示裝置與 接收裝置時,發出信號的裝置會顯示「輕觸即可傳輸」第一種是使用無代碼解決方案 AutoML 透過使用者介面建立機器學習模型使用者就能選擇 是否要將訊息傳輸至接收裝置。

注意:前景 NDEF 推送功能適用於 API 級別 10。 這套系統提供與 Android Beam 類似的功能這些 API 已淘汰 都能支援舊型裝置詳情請參閱《enableForegroundNdefPush()》。

您可以呼叫以下兩種方法之一,為應用程式啟用 Android Beam:

一個活動一次只能推送一則 NDEF 訊息,因此 setNdefPushMessageCallback() 的優先順序最高 在 setNdefPushMessage() 上設定兩者。使用條件 必須符合下列一般準則,才能使用 Android Beam:

  • 操控資料的活動必須位於前景。兩部裝置必須同時具備 他們的螢幕處於解鎖狀態。
  • 您必須封裝要在 NdefMessage 中剖析的資料 物件。
  • 接收橫梁資料的 NFC 裝置必須支援 com.android.npp NDEF 推送通訊協定或 NFC 論壇的 SNEP (簡易 NDEF 交換) 通訊協定)。搭載 API 級別 9 (Android) 的裝置必須使用 com.android.npp 通訊協定 2.3) 至 API 級別 13 (Android 3.2)。以下項目都必須啟用 com.android.npp 和 SNEP API 級別 14 (Android 4.0) 以上版本。

注意:如果您的活動會啟用 Android Beam,且 前景意圖分派系統已停用。不過,如果您的活動 啟用 前景調度,則仍可掃描與先前在 Pod 中設定的意圖篩選器相符的標記 是在前景調度

如何啟用 Android Beam:

  1. 建立包含 NdefRecordNdefMessage 可以推送到其他裝置
  2. 使用 NdefMessage 呼叫 setNdefPushMessage(),或呼叫 setNdefPushMessageCallback,傳入的 onCreate() 方法中的 NfcAdapter.CreateNdefMessageCallback 物件 您的活動。您至少須啟用一項活動,才能使用這些方法 Beam 以及其他要啟動的活動清單 (選用)。

    一般來說,如果 Activity 只需要使用setNdefPushMessage() 在兩部裝置在範圍內進行通訊時隨時傳送相同的 NDEF 訊息。使用 setNdefPushMessageCallback: 應用程式在乎應用程式的目前環境,並想要推送 NDEF 訊息 根據使用者在應用程式中的動作進行調整

以下範例說明簡易活動如何呼叫 NfcAdapter.CreateNdefMessageCallbackonCreate() 方法 活動 (請參閱 AndroidBeamDemo) 查看完整範例)。這個範例也提供有助於建立 MIME 記錄的方法:

Kotlin

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

class Beam : Activity(), NfcAdapter.CreateNdefMessageCallback {
    
    private var nfcAdapter: NfcAdapter? = null
    private lateinit var textView: TextView

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.main)
        textView = findViewById(R.id.textView)
        // Check for available NFC Adapter
        nfcAdapter = NfcAdapter.getDefaultAdapter(this)
        if (nfcAdapter == null) {
            Toast.makeText(this, "NFC is not available", Toast.LENGTH_LONG).show()
            finish()
            return
        }
        // Register callback
        nfcAdapter?.setNdefPushMessageCallback(this, this)
    }

    override fun createNdefMessage(event: NfcEvent): NdefMessage {
        val text = "Beam me up, Android!\n\n" +
                "Beam Time: " + System.currentTimeMillis()
        return NdefMessage(
                arrayOf(
                        createMime("application/vnd.com.example.android.beam", text.toByteArray())
                )
                /**
                 * 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")
        )
    }

    override fun onResume() {
        super.onResume()
        // Check to see that the Activity started due to an Android Beam
        if (NfcAdapter.ACTION_NDEF_DISCOVERED == intent.action) {
            processIntent(intent)
        }
    }

    override fun 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
     */
    private fun processIntent(intent: Intent) {
        textView = findViewById(R.id.textView)
        // only one message sent during the beam
        intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES)?.also { rawMsgs ->
            (rawMsgs[0] as NdefMessage).apply {
                // record 0 contains the MIME type, record 1 is the AAR, if present
                textView.text = String(records[0].payload)
            }
        }
    }
}

Java

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 nfcAdapter;
    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
        nfcAdapter = NfcAdapter.getDefaultAdapter(this);
        if (nfcAdapter == null) {
            Toast.makeText(this, "NFC is not available", Toast.LENGTH_LONG).show();
            finish();
            return;
        }
        // Register callback
        nfcAdapter.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 後 一律會收到 Android Beam 訊息。如果不是, 管理員已開始下載這個應用程式。因此,下列意圖 如果使用 AAR,則技術上通常不需要 Android 4.0 以上版本裝置:

<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>

透過這個意圖篩選器,您現在可以啟動 com.example.android.beam 應用程式 掃描 NFC 標記或收到具有 AAR 的 AAR 的 Android Beam 輸入 com.example.android.beam,或當 NDEF 格式的郵件包含 MIME 記錄時 類型為 application/vnd.com.example.android.beam

即使 AAR 保證應用程式已經啟動或下載,意圖篩選器 建議原因,因為您可以在 應用程式,而不是一律在 AAR 指定的套件中啟動主要 Activity。 AAR 沒有活動層級的精細程度。此外,由於某些搭載 Android 的裝置 支援 AAR,您也需要在 NDEF 記錄的第一筆 NDEF 記錄中嵌入識別資訊 並且篩選郵件,以防萬一。請參閱建立常見的 NDEF 記錄類型,進一步瞭解如何建立記錄。