NFC 基本概念

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

系統會透過標記調度系統處理從 NFC 標記讀取 NDEF 資料的作業,該系統會分析所偵測到的 NFC 標記,適當分類資料,並啟動對分類資料感興趣的應用程式。想要處理掃描到的 NFC 標記的應用程式可以宣告意圖篩選器,並要求處理資料。

代碼調度系統

除非在裝置的「設定」選單中停用 NFC,否則 Android 裝置通常會在螢幕解鎖時尋找 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 資料會封裝在包含一或多個記錄 (NdefRecord) 的訊息 (NdefMessage) 中。每個 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 根據類型欄位的 URN 建立的 URI。URN 會以簡寫形式編碼至 NDEF 類型欄位:<domain_name>:<service_name>。Android 會將這個值對應至格式為 vnd.android.nfc://ext/<domain_name>:<service_name> 的 URI。
TNF_MIME_MEDIA 根據 type 欄位指定的 MIME 類型。
TNF_UNCHANGED 在第一筆記錄中無效,因此會改回 ACTION_TECH_DISCOVERED
TNF_UNKNOWN 回復為 ACTION_TECH_DISCOVERED
TNF_WELL_KNOWN MIME 類型或 URI,取決於您在 type 欄位中設定的記錄類型定義 (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 酬載的標籤,且屬於已知類型時,這個意圖會用於啟動 Activity。這是優先順序最高的意圖,且標記調度系統會盡可能在任何其他意圖之前,嘗試以此意圖啟動活動。

    注意:自 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 存取權

您必須先在 AndroidManifest.xml 檔案中宣告下列項目,才能存取裝置的 NFC 硬體並正確處理 NFC 意圖:

  • 用於存取 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 元素,並檢查 getDefaultAdapter() 是否為 null,以便在執行階段檢查 NFC 是否可用。

篩選 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 意圖,請宣告意圖篩選器,並指定要篩選的資料類型。以下範例篩選 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 形式的 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 意圖啟動,以確保已掃描標籤,然後從意圖取得額外項目。以下範例會檢查 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 搭配 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>

使用 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,這是範例中意圖篩選器宣告的內容。

Android 應用程式記錄

Android 4.0 (API 級別 14) 推出的 Android 應用程式記錄 (AAR) 可確保在掃描 NFC 標記時啟動應用程式。AAR 包含 NDEF 記錄內嵌的應用程式套件名稱。您可以在 NDEF 訊息的任何 NDEF 記錄中新增 AAR,因為 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()。您只需將 AAR 嵌入 NdefMessage 中的任何位置即可。除非 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 標記。

注意:NFC 標記掃描應用程式許可清單位於 Settings > Apps > Special app access > Launch via NFC 下方。

我們新增這個機制,以解決使用者提出的疑慮。當使用者將手機放在 NFC 標記 (信用卡、其他手機/手錶等) 旁邊時,部分已註冊 NFC 標記意圖意圖濾鏡的應用程式會不斷顯示在前景。