Omówienie zaawansowanych funkcji NFC

W tym dokumencie opisano zaawansowane zagadnienia związane z NFC, takie jak praca z różnymi technologiami tagów, zapisywanie do tagów NFC oraz wysyłanie na pierwszym planie, które umożliwia aplikacji na pierwszym planie obsługę intencji nawet wtedy, gdy inne aplikacje odfiltrowują te same intencje.

Praca z obsługiwanymi technologiami tagów

W przypadku korzystania z tagów NFC i urządzeń z Androidem głównym formatem używanym do odczytywania i zapisywania danych w tagach jest NDEF. Gdy urządzenie skanuje tag z danymi NDEF, Android umożliwia przeanalizowanie komunikatu i dostarczenie go w elemencie NdefMessage, gdy to możliwe. Zdarzają się jednak sytuacje, gdy skanujesz tag, który nie zawiera danych NDEF, lub gdy danych NDEF nie można zmapować na typ MIME lub identyfikator URI. W takich przypadkach musisz nawiązać bezpośrednią komunikację z tagiem oraz odczytywać i zapisywać w nim dane za pomocą własnego protokołu (w nieprzetworzonych bajtach). Android zapewnia ogólną obsługę tych przypadków użycia w pakiecie android.nfc.tech, który został opisany w tabeli 1. Możesz użyć metody getTechList(), aby określić technologie obsługiwane przez tag i utworzyć odpowiedni obiekt TagTechnology za pomocą jednej z klas dostępnych w android.nfc.tech.

Tabela 1. Obsługiwane technologie tagów

Kategoria Opis
TagTechnology Interfejs, który muszą implementować wszystkie klasy technologii tagów.
NfcA Zapewnia dostęp do właściwości NFC-A (ISO 14443-3A) i do operacji wejścia-wyjścia.
NfcB Zapewnia dostęp do właściwości NFC-B (ISO 14443-3B) i operacji wejścia-wyjścia.
NfcF Zapewnia dostęp do właściwości NFC-F (JIS 6319-4) i operacji wejścia-wyjścia.
NfcV Zapewnia dostęp do właściwości NFC-V (ISO 15693) i operacji wejścia-wyjścia.
IsoDep Zapewnia dostęp do właściwości ISO-DEP (ISO 14443-4) i operacji wejścia-wyjścia.
Ndef Daje dostęp do danych i operacji NDEF na tagach NFC sformatowanych jako NDEF.
NdefFormatable Udostępnia operacje formatowania tagów, które mogą być formatami NDEF.

Urządzenia z Androidem nie muszą obsługiwać poniższych technologii tagów.

Tabela 2. Opcjonalne obsługiwane technologie tagów

Kategoria Opis
MifareClassic Daje dostęp do właściwości klasy MIFARE i operacji wejścia-wyjścia, jeśli to urządzenie z Androidem obsługuje MIFARE.
MifareUltralight Daje dostęp do właściwości MIFARE Ultralight i operacji wejścia-wyjścia, jeśli to urządzenie z Androidem obsługuje MIFARE.

Korzystanie z technologii tagów i intencji ACTION_TECH_DISCOVERED

Gdy urządzenie skanuje tag zawierający dane NDEF, ale nie udało się go zmapować na MIME lub identyfikator URI, system wysyłania tagów próbuje uruchomić działanie z intencją ACTION_TECH_DISCOVERED. Parametr ACTION_TECH_DISCOVERED jest też używany podczas skanowania tagu z danymi innymi niż NDEF. Użycie tej wartości zastępczej umożliwia pracę z danymi w tagu bezpośrednio, jeśli system wysyłania tagów nie mógł ich przeanalizować. Podstawowe kroki przy korzystaniu z technologii tagów:

  1. Odfiltruj intencję ACTION_TECH_DISCOVERED, określając technologie tagów, które chcesz obsługiwać. Więcej informacji znajdziesz w sekcji Filtrowanie intencji NFC. Ogólnie rzecz biorąc, system wysyłania tagów próbuje uruchomić intencję ACTION_TECH_DISCOVERED, gdy wiadomości NDEF nie można zmapować na typ MIME lub identyfikator URI albo gdy przeskanowany tag nie zawiera danych NDEF. Więcej informacji o sposobie tego określania znajdziesz w artykule System wysyłania tagów.
  2. Gdy aplikacja otrzyma intencję, pobierz z niej obiekt Tag:

    Kotlin

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

    Java

    Tag tagFromIntent = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
    
  3. Uzyskaj instancję TagTechnology, wywołując jedną z metod fabrycznych get klasy w pakiecie android.nfc.tech. Możesz wyliczyć obsługiwane technologie tagu, wywołując getTechList() przed wywołaniem metody fabrycznej get. Aby np. uzyskać wystąpienie MifareUltralight z Tag, wykonaj te czynności:

    Kotlin

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

    Java

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

Odczyt i zapis w tagach

Odczyt i zapis w tagu NFC obejmują uzyskanie tagu z intencji i nawiązanie z nim komunikacji. Musisz zdefiniować własny stos protokołów, aby odczytywać i zapisywać dane w tagu. Pamiętaj jednak, że nadal możesz odczytywać i zapisywać dane NDEF, pracując bezpośrednio z tagiem. To zależy od Ciebie. Poniższy przykład pokazuje, jak korzystać z tagu 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;
    }
}

Korzystanie z systemu wysyłania na pierwszym planie

System wysyłania na pierwszym planie umożliwia aktywności przechwytywanie intencji i priorytet żądania względem innych działań, które obsługują tę samą intencję. Wykorzystanie tego systemu wymaga zbudowania kilku struktur danych dla systemu Android, które będą mogły wysyłać odpowiednie intencje do Twojej aplikacji. Aby włączyć system wysyłania na pierwszym planie:

  1. Dodaj ten kod w metodzie onCreate() aktywności:
    1. Utwórz zmienny obiekt PendingIntent, aby system Android mógł wypełniać go szczegółami tagu podczas skanowania.

      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. Zadeklaruj filtry intencji, aby obsługiwać intencje, które chcesz przechwytywać. System wysyłania na pierwszym planie sprawdza określone filtry intencji pod kątem intencji otrzymanej, gdy urządzenie skanuje tag. Jeśli jest zgodny, aplikacja obsługuje intencję. Jeśli nie, system wysyłania na pierwszym planie wraca do systemu wysyłania intencji. Jeśli określisz tablicę null w filtrach intencji i technologiach, chcesz odfiltrowywać wszystkie tagi, które odwołują się do intencji TAG_DISCOVERED. Poniższy fragment kodu obsługuje wszystkie typy MIME w domenie NDEF_DISCOVERED. Używaj tylko tych, które są Ci potrzebne.

      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. Skonfiguruj tablicę technologii tagów, które ma obsługiwać Twoja aplikacja. Wywołaj metodę Object.class.getName(), aby uzyskać klasę technologii, którą chcesz obsługiwać.

      Kotlin

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

      Java

      techListsArray = new String[][] { new String[] { NfcF.class.getName() } };
      
  2. Zastąp poniższe wywołania zwrotne cyklu życia aktywności i dodaj logikę, aby włączyć lub wyłączyć wysyłanie na pierwszym planie, gdy aktywność utraci (onPause()) i ponownie skoncentruje się (onResume()). Funkcję enableForegroundDispatch() należy wywołać z wątku głównego i tylko wtedy, gdy aktywność działa na pierwszym planie (wywołanie w metodzie onResume() jest gwarantowane). Aby przetworzyć dane ze zeskanowanego tagu NFC, musisz też zaimplementować wywołanie zwrotne onNewIntent.
  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
    }