NFC 基本概念

本文說明您在 Android 中執行的基本 NFC 工作。本文說明如何以 NDEF 訊息的形式傳送及接收 NFC 資料,並介紹支援這些功能的 Android 架構 API。如要瞭解進階主題,包括如何處理非 NDEF 資料,請參閱「進階 NFC」。

從 NFC 標記讀取 NDEF 資料時,系統會使用標記調度系統,分析發現的 NFC 標記、適當分類資料,並啟動對分類資料感興趣的應用程式。如要處理掃描到的 NFC 標記,應用程式可以宣告意圖篩選器,並要求處理資料。

代碼調度系統

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 會以縮寫形式編碼至 NDEF 類型欄位:<domain_name>:<service_name>。Android 會將此對應至 vnd.android.nfc://ext/<domain_name>:<service_name> 格式的 URI。
TNF_MIME_MEDIA 根據類型欄位判斷的 MIME 類型。
TNF_UNCHANGED 第一個記錄無效,因此會改回使用 ACTION_TECH_DISCOVERED
TNF_UNKNOWN 改用 ACTION_TECH_DISCOVERED
TNF_WELL_KNOWN MIME 類型或 URI,視您在類型欄位中設定的記錄類型定義 (RTD) 而定。如要進一步瞭解可用的 RTD 及其對應項目,請參閱表 2

表 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 酬載且屬於可辨識類型的標記時,系統會使用這個意圖啟動活動。這是最高優先順序的意圖,只要可能,代碼傳送系統就會先嘗試使用這個意圖啟動活動,再使用其他意圖。

    注意:自 Android 16 起,掃描儲存網址連結的 NFC 標記 (即 URI 架構為「htttps://」或「http://」) 時,系統會觸發 ACTION_VIEW 意圖,而非 ACTION_NDEF_DISCOVERED 意圖。

  2. ACTION_TECH_DISCOVERED:如果沒有任何活動註冊處理 ACTION_NDEF_DISCOVERED 意圖,標記分派系統會嘗試使用這個意圖啟動應用程式。如果掃描的標記含有無法對應至 MIME 類型或 URI 的 NDEF 資料,或標記不含 NDEF 資料,但屬於已知標記技術,系統也會直接啟動這項意圖 (不需先啟動 ACTION_NDEF_DISCOVERED)。
  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 Intent,請在 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 則提供額外的便利方法,可建立 NDEF 記錄。
    <uses-sdk android:minSdkVersion="10"/>
  • uses-feature 元素,讓應用程式只在具備 NFC 硬體的裝置上顯示於 Google Play:
    <uses-feature android:name="android.hardware.nfc" android:required="true" />

    如果應用程式使用 NFC 功能,但該功能並非應用程式的必要功能,您可以省略 uses-feature 元素,並在執行階段檢查 NFC 是否可用,方法是檢查 getDefaultAdapter() 是否為 null

篩選 NFC 意圖

如要在掃描您要處理的 NFC 標記時啟動應用程式,應用程式可以在 Android 資訊清單中,篩選一、二或全部三種 NFC 意圖。不過,您通常會想篩選 ACTION_NDEF_DISCOVERED 意圖,以便盡可能控管應用程式的啟動時間。如果沒有應用程式篩選器適用於 ACTION_NDEF_DISCOVERED,或酬載不是 NDEF,ACTION_TECH_DISCOVERED 意圖就會做為 ACTION_NDEF_DISCOVERED 的備用意圖。篩選 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 意圖,請一併宣告意圖篩選器和要篩選的資料類型。下列範例會篩選 MIME 類型為 text/plainACTION_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>

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

ACTION_TECH_DISCOVERED

如果活動會篩選 ACTION_TECH_DISCOVERED 意圖,您必須建立 XML 資源檔案,在 tech-list 集中指定活動支援的技術。如果 tech-list 集是代碼支援技術的子集 (可透過呼叫 getTechList() 取得),系統就會將您的活動視為相符。

舉例來說,如果掃描的標記支援 MifareClassic、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_DISCOVERED

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

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

ACTION_VIEW

從 Android 16 開始,掃描儲存網址連結的 NFC 標記會觸發 ACTION_VIEW 意圖。如要篩選 ACTION_VIEW,請參閱this。使用 Android app links 開啟網址的應用程式。

從意圖取得資訊

如果活動是因 NFC 意圖而啟動,您可以從意圖取得掃描到的 NFC 標籤相關資訊。意圖可包含下列額外資訊,視掃描的標記而定:

如要取得這些額外資訊,請檢查活動是否已透過其中一個 NFC Intent 啟動,確保系統已掃描標記,然後從 Intent 取得額外資訊。下列範例會檢查 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 4.0 (API 級別 14) 開始,您可以使用 createUri() 方法自動建立 URI 記錄。從 Android 4.1 (API 級別 16) 開始,您可以使用 createExternal()createMime() 建立 MIME 和外部類型 NDEF 記錄。請盡可能使用這些輔助方法,避免手動建立 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 with 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>

TNF_WELL_KNOWN,附有 RTD_URI

您可以透過下列方式建立 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_EXTERNAL_TYPE

您可以透過下列方式建立 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>

如要更廣泛地部署 NFC 標記,請使用 TNF_EXTERNAL_TYPE,以便同時支援 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,這也是範例中意圖篩選器宣告的內容。

Android 應用程式記錄

Android 4.0 (API 級別 14) 推出的 Android 應用程式記錄 (AAR),可確保系統掃描 NFC 標記時啟動您的應用程式。AAR 的套件名稱會嵌入 NDEF 記錄中。您可以將 AAR 新增至 NDEF 訊息的任何 NDEF 記錄,因為 Android 會在整個 NDEF 訊息中搜尋 AAR。如果找到 AAR,系統會根據 AAR 內的套件名稱啟動應用程式。如果裝置上沒有該應用程式,系統會啟動 Google Play 下載應用程式。

如果您想避免其他應用程式篩選相同意圖,並可能處理您部署的特定標記,AAR 就很有用。由於套件名稱限制,AAR 僅支援應用程式層級,不支援活動層級 (如意圖篩選)。如要在 Activity 層級處理意圖,請使用意圖篩選器

如果代碼含有 AAR,代碼傳送系統會以以下方式傳送:

  1. 請嘗試照常使用意圖篩選器啟動活動。如果與意圖相符的活動也符合 AAR,請啟動該活動。
  2. 如果篩選意圖的活動與 AAR 不符、有多個活動可處理意圖,或沒有任何活動可處理意圖,請啟動 AAR 指定的應用程式。
  3. 如果沒有任何應用程式可以啟動 AAR,請前往 Google Play 下載 AAR 適用的應用程式。

注意:您可以透過前景分派系統覆寫 AAR 和意圖分派系統,讓前景活動在系統偵測到 NFC 標記時取得優先權。使用這個方法時,活動必須位於前景,才能覆寫 AAR 和意圖分派系統。

如果仍要篩選不含 AAR 的掃描標記,可以照常宣告意圖篩選器。如果應用程式對不含 AAR 的其他標記感興趣,這項功能就非常實用。舉例來說,您可能想確保應用程式能處理您部署的專屬標記,以及第三方部署的一般標記。請注意,AAR 僅適用於 Android 4.0 以上版本的裝置,因此部署標記時,您可能需要同時使用 AAR 和 MIME 類型/URI,才能支援最多裝置。此外,部署 NFC 標記時,請考慮如何寫入 NFC 標記,以支援最多裝置 (Android 裝置和其他裝置)。您可以定義相對獨特的 MIME 類型或 URI,方便應用程式區分。

Android 提供簡單的 API 來建立 AAR, createApplicationRecord(). 您只需在 NdefMessage 中嵌入 AAR 即可。除非 AAR 是 NdefMessage 中唯一的記錄,否則您不應使用 NdefMessage 的第一筆記錄。這是因為 Android 系統會檢查 NdefMessage 的第一筆記錄,判斷標記的 MIME 類型或 URI,並用於建立意圖,供應用程式篩選。下列程式碼說明如何建立 AAR:

Kotlin

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

Java

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

允許掃描 NFC 標記的應用程式清單

從 Android 16 開始,應用程式首次收到掃描 NFC 標記的 NFC 意圖時,使用者會收到通知。使用者可以在通知中選擇禁止應用程式掃描 NFC 標記。

注意:您可以在 Settings > Apps > Special app access > Launch via NFC 下方存取 NFC 標記掃描應用程式許可清單。

使用者反映,部分應用程式已為 NFC 標記意圖註冊意圖篩選器,但當他們將手機靠近 NFC 標記 (信用卡、其他手機/手錶等) 時,這些應用程式會重複顯示在前景。為解決這類問題,我們新增了這項機制。