進階 NFC 總覽

本文件說明進階 NFC 主題,例如使用各種標記技術。 以及前景調度,讓前景的應用程式能 處理意圖,即使其他應用程式篩選相同項目也是如此。

使用支援的代碼技術

使用 NFC 標記和 Android 裝置時,系統會使用用於讀取的主要格式 並將標記資料寫入 NDEF當裝置掃描含有 NDEF 資料的標記時,Android 提供了相關支援 剖析訊息並在 NdefMessage 中傳送 但在某些情況下,掃描的廣告代碼會不包含 NDEF 資料,或是 NDEF 資料無法對應至 MIME 類型或 URI。 在這些情況下,您必須直接與標記進行溝通,然後使用標記讀取及寫入該標記 您自己的通訊協定 (以原始位元組為單位)Android 針對這些用途提供一般支援。 android.nfc.tech 套件 (相關說明請見表 1)。你可以 使用 getTechList() 方法判斷技術 ,並建立對應的TagTechnology 具有 android.nfc.tech 提供的任一類別的物件

表 1. 支援的代碼技術

類別 說明
TagTechnology 所有代碼技術類別都必須導入的介面。
NfcA 提供 NFC-A (ISO 14443-3A) 屬性和 I/O 作業的存取權。
NfcB 提供 NFC-B (ISO 14443-3B) 屬性和 I/O 作業的存取權。
NfcF 提供 NFC-F (JIS 6319-4) 屬性和 I/O 作業的存取權。
NfcV 提供 NFC-V (ISO 15693) 屬性和 I/O 作業的存取權。
IsoDep 提供 ISO-DEP (ISO 14443-4) 屬性和 I/O 作業的存取權。
Ndef 讓您存取 NDEF 資料和作業,對採用以下格式的 NFC 標記執行操作: NDEF。
NdefFormatable 為可能是 NDEF 格式的代碼提供格式作業。

Android 裝置不必支援下列標記技術。

表 2. 支援的選用代碼技術

類別 說明
MifareClassic 可讓 MIFARE 傳統屬性 (如果這部 Android 裝置) 存取 MIFARE 傳統屬性和 I/O 作業 支援 MIFARE。
MifareUltralight 提供 MIFARE Ultralight 屬性和 I/O 作業 (如果這個 Android 裝置) 裝置支援 MIFARE。

使用標記技術和 ACTION_TECH_DISCOVERED 意圖

當裝置掃描含有 NDEF 資料,但無法對應至 MIME 或 URI 的標記時, 代碼分派系統會嘗試使用 ACTION_TECH_DISCOVERED 啟動活動 意圖。當標記時,也會使用 ACTION_TECH_DISCOVERED 掃描非 NDEF 資料設定這種備用值後,您就能使用代碼上的資料 (廣告代碼分派系統無法為您剖析)。基本步驟 代碼技術如下:

  1. 篩選一個 ACTION_TECH_DISCOVERED 意圖,並指定該意圖 要處理的代碼技術請參閱篩選 NFC 意圖。一般來說,在收到 NDEF 訊息時,標記分派系統會嘗試啟動 ACTION_TECH_DISCOVERED 意圖 無法對應至 MIME 類型或 URI,或是掃描的標記不含 NDEF 資料。適用對象 如要進一步瞭解判定方式,請參閱標記調度系統
  2. 應用程式收到意圖時,請從以下位置取得 Tag 物件: 意圖:

    Kotlin

    var tagFromIntent: Tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG)
    

    Java

    Tag tagFromIntent = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
    
  3. 如要取得 TagTechnology 的例項,請呼叫 android.nfc.tech 套件中類別的 get 工廠方法。你可以 請在呼叫 get 工廠方法之前呼叫 getTechList(),列舉標記支援的技術。例如:如要取得執行個體 來自 TagMifareUltralight,請執行以下操作:

    Kotlin

    MifareUltralight.get(intent.getParcelableExtra(NfcAdapter.EXTRA_TAG))
    

    Java

    MifareUltralight.get(intent.getParcelableExtra(NfcAdapter.EXTRA_TAG));
    

讀取及寫入標記

讀取和寫入 NFC 標記時,您必須從意圖取得該標記,並 和代碼展開通訊您必須定義自己的通訊協定堆疊,才能讀取及寫入資料 加入廣告代碼不過請記住,您在工作時仍可讀取及寫入 NDEF 資料 直接加上代碼要如何建構內容,完全由您決定。 以下範例說明如何使用 MIFARE Ultralight 代碼。

Kotlin

package com.example.android.nfc
import android.nfc.Tag
import android.nfc.tech.MifareUltralight
import java.io.IOException
import java.nio.charset.Charset

class MifareUltralightTagTester {

    fun writeTag(tag: Tag, tagText: String) {
        MifareUltralight.get(tag)?.use { ultralight ->
            ultralight.connect()
            Charset.forName("US-ASCII").also { usAscii ->
                ultralight.writePage(4, "abcd".toByteArray(usAscii))
                ultralight.writePage(5, "efgh".toByteArray(usAscii))
                ultralight.writePage(6, "ijkl".toByteArray(usAscii))
                ultralight.writePage(7, "mnop".toByteArray(usAscii))
            }
        }
    }

    fun readTag(tag: Tag): String? {
        return MifareUltralight.get(tag)?.use { mifare ->
            mifare.connect()
            val payload = mifare.readPages(4)
            String(payload, Charset.forName("US-ASCII"))
        }
    }
}

Java

package com.example.android.nfc;

import android.nfc.Tag;
import android.nfc.tech.MifareUltralight;
import android.util.Log;
import java.io.IOException;
import java.nio.charset.Charset;

public class MifareUltralightTagTester {

    private static final String TAG = MifareUltralightTagTester.class.getSimpleName();

    public void writeTag(Tag tag, String tagText) {
        MifareUltralight ultralight = MifareUltralight.get(tag);
        try {
            ultralight.connect();
            ultralight.writePage(4, "abcd".getBytes(Charset.forName("US-ASCII")));
            ultralight.writePage(5, "efgh".getBytes(Charset.forName("US-ASCII")));
            ultralight.writePage(6, "ijkl".getBytes(Charset.forName("US-ASCII")));
            ultralight.writePage(7, "mnop".getBytes(Charset.forName("US-ASCII")));
        } catch (IOException e) {
            Log.e(TAG, "IOException while writing MifareUltralight...", e);
        } finally {
            try {
                ultralight.close();
            } catch (IOException e) {
                Log.e(TAG, "IOException while closing MifareUltralight...", e);
            }
        }
    }

    public String readTag(Tag tag) {
        MifareUltralight mifare = MifareUltralight.get(tag);
        try {
            mifare.connect();
            byte[] payload = mifare.readPages(4);
            return new String(payload, Charset.forName("US-ASCII"));
        } catch (IOException e) {
            Log.e(TAG, "IOException while reading MifareUltralight message...", e);
        } finally {
            if (mifare != null) {
               try {
                   mifare.close();
               }
               catch (IOException e) {
                   Log.e(TAG, "Error closing tag...", e);
               }
            }
        }
        return null;
    }
}

使用前景調度系統

前景調度系統可讓活動攔截意圖和聲明 優先順序高於處理相同意圖的其他活動。使用這個系統牽涉到 為 Android 系統建構一些資料結構,以便傳送適當的資料 意圖傳送至您的應用程式如何啟用前景調度系統:

  1. 在活動的 onCreate() 方法中加入下列程式碼:
    1. 建立可變動的 PendingIntent 物件,讓 Android 系統填入該物件 以及標籤詳細資料

      Kotlin

      val intent = Intent(this, javaClass).apply {
          addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP)
      }
      var pendingIntent: PendingIntent = PendingIntent.getActivity(this, 0, intent,
              PendingIntent.FLAG_MUTABLE)
      

      Java

      PendingIntent pendingIntent = PendingIntent.getActivity(
          this, 0, new Intent(this, getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP),
          PendingIntent.FLAG_MUTABLE);
      
    2. 宣告意圖篩選器以處理要攔截的意圖。前景 派出系統,會根據收到的意圖檢查指定的意圖篩選器 裝置就會掃描標記如果相符,應用程式就會處理該意圖。需要的話 不相符,前景調度系統會改回使用意圖調度系統。 指定意圖篩選器和技術篩選器的 null 陣列,並指定 篩選出所有會傳回 TAG_DISCOVERED 的代碼 意圖。以下程式碼片段會處理 NDEF_DISCOVERED 的所有 MIME 類型。個人中心 應該只處理您需要的工作負載

      Kotlin

      val ndef = IntentFilter(NfcAdapter.ACTION_NDEF_DISCOVERED).apply {
          try {
              addDataType("*/*")    /* Handles all MIME based dispatches.
                                       You should specify only the ones that you need. */
          } catch (e: IntentFilter.MalformedMimeTypeException) {
              throw RuntimeException("fail", e)
          }
      }
      
      intentFiltersArray = arrayOf(ndef)
      

      Java

      IntentFilter ndef = new IntentFilter(NfcAdapter.ACTION_NDEF_DISCOVERED);
          try {
              ndef.addDataType("*/*");    /* Handles all MIME based dispatches.
                                             You should specify only the ones that you need. */
          }
          catch (MalformedMimeTypeException e) {
              throw new RuntimeException("fail", e);
          }
         intentFiltersArray = new IntentFilter[] {ndef, };
      
    3. 設定應用程式要處理的代碼技術陣列。在 Object.class.getName() 方法,取得所需的技術類別 機構帳戶

      Kotlin

      techListsArray = arrayOf(arrayOf<String>(NfcF::class.java.name))
      

      Java

      techListsArray = new String[][] { new String[] { NfcF.class.getName() } };
      
  2. 覆寫以下活動生命週期回呼,並新增邏輯以啟用及停用 活動遺失時 (onPause()) 執行前景調度 並重新取得 (onResume()) 對焦。必須從以下位置呼叫 enableForegroundDispatch(): 主執行緒,且只在活動於前景運作時 (在 onResume() 中呼叫可保證此情況)。您也需要實作 onNewIntent 回呼,以便處理掃描的 NFC 資料 標記之前。
  3. Kotlin

    public override fun onPause() {
        super.onPause()
        adapter.disableForegroundDispatch(this)
    }
    
    public override fun onResume() {
        super.onResume()
        adapter.enableForegroundDispatch(this, pendingIntent, intentFiltersArray, techListsArray)
    }
    
    public override fun onNewIntent(intent: Intent) {
        val tagFromIntent: Tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG)
        // do something with tagFromIntent
    }
    

    Java

    public void onPause() {
        super.onPause();
        adapter.disableForegroundDispatch(this);
    }
    
    public void onResume() {
        super.onResume();
        adapter.enableForegroundDispatch(this, pendingIntent, intentFiltersArray, techListsArray);
    }
    
    public void onNewIntent(Intent intent) {
        Tag tagFromIntent = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
        // do something with tagFromIntent
    }