Podstawy komunikacji NFC

W tym dokumencie opisano podstawowe zadania związane z NFC, które wykonuje się na Androidzie. Wyjaśniono w nim, jak wysyłać i odbierać dane NFC w postaci wiadomości NDEF, oraz opisujemy interfejsy Android Framework API, które obsługują te funkcje. Bardziej zaawansowane tematy, w tym omówienie pracy z danymi innymi niż NDEF, znajdziesz w sekcji Zaawansowane opcje NFC.

Są 2 główne przypadki użycia danych NDEF i Androida:

  • Odczytywanie danych NDEF z tagu NFC
  • Przesyłanie wiadomości NDEF z jednego urządzenia do drugiego za pomocą Android BeamTM

Odczytywanie danych NDEF z tagu NFC jest obsługiwane przez system wysyłania tagów, który analizuje wykryte tagi NFC, odpowiednio je kategoryzuje i uruchamia aplikację, która jest zainteresowana odpowiednimi danymi. Aplikacja, która chce obsługiwać zeskanowany tag NFC, może zadeklarować filtr intencji i poprosić o obsługę danych.

Funkcja Android BeamTM pozwala urządzeniu przekazać wiadomość NDEF na inne urządzenie przez fizyczne złączenie ich ze sobą. Taka interakcja ułatwia wysyłanie danych niż przy użyciu innych technologii bezprzewodowych, takich jak Bluetooth, ponieważ przy użyciu NFC nie trzeba ręcznie wykrywać ani parowania urządzeń. Połączenie rozpoczyna się automatycznie, gdy dwa urządzenia znajdują się w zasięgu. Funkcja Android Beam jest dostępna przez zestaw interfejsów API NFC, więc każda aplikacja może przesyłać informacje między urządzeniami. Na przykład aplikacje Kontakty, Przeglądarka i YouTube korzystają z Android Beam do udostępniania kontaktów, stron internetowych i filmów innym urządzeniom.

System wysyłania tagów

Urządzenia z Androidem szukają tagów NFC, gdy ekran jest odblokowany, chyba że komunikacja NFC jest wyłączona w menu ustawień. Gdy urządzenie z Androidem wykryje tag NFC, pożądane zachowanie polega na tym, aby najbardziej odpowiednia aktywność obsługiwała intencję, bez pytania użytkownika o to, jakiej aplikacji ma użyć. Urządzenia skanują tagi NFC z bardzo małego zakresu, więc zachęcenie użytkowników do ręcznego wybrania aktywności może zmusić użytkownika do odsunięcia urządzenia od tagu i zerwania połączenia. Rozwiń aktywność w taki sposób, aby obsługiwała tylko te tagi NFC, które są istotne dla Twojej aktywności, tak aby selektor aktywności nie był wyświetlany.

Aby Ci w tym pomóc, Android udostępnia specjalny system wysyłania tagów, który analizuje zeskanowane tagi NFC, analizuje je i próbuje znaleźć aplikacje zainteresowane zeskanowanymi danymi. Realizuje to:

  1. Analiza tagu NFC i określenie typu MIME lub identyfikatora URI identyfikującego ładunek danych w tagu.
  2. Umieszczanie w intencji typu MIME lub identyfikatora URI oraz ładunku. Te 2 pierwsze kroki opisaliśmy w sekcji Jak tagi NFC są mapowane na typy MIME i identyfikatory URI.
  3. Uruchamia działanie na podstawie intencji. Zostało to opisane w artykule Jak tagi NFC są wysyłane do aplikacji.

Jak tagi NFC są mapowane na typy MIME i identyfikatory URI

Zanim zaczniesz pisać aplikacje do NFC, zapoznaj się z różnymi rodzajami tagów NFC, sposobem analizowania tych tagów przez system wysyłania tagów i specjalną pracą, jaką system wysyłania tagów wykonuje w przypadku wykrycia komunikatu NDEF. Tagi NFC mają szeroką gamę technologii i można w nich zapisywać dane na wiele różnych sposobów. Android zapewnia największą obsługę standardu NDEF zdefiniowanego na Forum NFC.

Dane NDEF są zawarte w wiadomości (NdefMessage) zawierającej co najmniej 1 rekord (NdefRecord). Każdy rekord NDEF musi być poprawnie sformatowany zgodnie ze specyfikacją typu rekordu, który chcesz utworzyć. Android obsługuje też inne typy tagów niezawierające danych NDEF, z którymi możesz pracować, używając klas w pakiecie android.nfc.tech. Więcej informacji o tych technologiach znajdziesz w sekcji Zaawansowane komunikację NFC. Praca z tymi innymi typami tagów wymaga napisania własnego stosu protokołów do komunikacji z tagami, dlatego zalecamy używanie NDEF, jeśli to możliwe, aby łatwiej je programować i zapewnić maksymalną obsługę urządzeń z Androidem.

Uwaga: aby pobrać pełne specyfikacje NDEF, wejdź na stronę NFC Forum Specifications & Application Documents i przeczytaj artykuł Tworzenie popularnych typów rekordów NDEF, aby zapoznać się z przykładami tworzenia rekordów NDEF.

Gdy już wiesz, jak działają tagi NFC, przeczytaj bardziej szczegółowe informacje w sekcjach poniżej, które szczegółowo opisują sposób, w jaki Android obsługuje tagi w formacie NDEF. Kiedy urządzenie z Androidem skanuje tag NFC zawierający dane w formacie NDEF, analizuje wiadomość i próbuje określić typ MIME danych lub identyfikator URI danych. W tym celu system odczytuje pierwsze NdefRecord w obrębie NdefMessage, aby określić, jak interpretować cały komunikat NDEF (wiadomość NDEF może mieć wiele rekordów NDEF). Prawidłowo skonstruowana wiadomość NDEF zawiera pierwsze NdefRecord z tymi polami:

3-bitowy TNF (format nazwy typu)
Wskazuje sposób interpretacji pola typu zmiennej długości. Prawidłowe wartości są opisane w tabeli 1.
Rodzaj zmiennej długości
Opisuje typ rekordu. Jeśli używasz TNF_WELL_KNOWN, wpisz w tym polu definicję typu rekordu (RTD). Prawidłowe wartości RTD zostały opisane w tabeli 2.
Identyfikator zmiennej długości
Unikalny identyfikator rekordu. To pole jest rzadko używane, ale jeśli chcesz jednoznacznie zidentyfikować tag, możesz utworzyć dla niego identyfikator.
Ładunek o zmiennej długości
Rzeczywisty ładunek danych, który chcesz odczytać lub zapisać. Wiadomość NDEF może zawierać wiele rekordów NDEF, dlatego nie zakładaj, że cały ładunek znajduje się w pierwszym rekordzie wiadomości NDEF.

System wysyłania tagów korzysta z pól TNF i typów, aby zmapować typ MIME lub identyfikator URI na wiadomość NDEF. Jeśli operacja się uda, umieszcza te informacje w intencji ACTION_NDEF_DISCOVERED wraz z rzeczywistym ładunkiem. Są jednak sytuacje, w których system wysyłania tagów nie może określić typu danych na podstawie pierwszego rekordu NDEF. Dzieje się tak, gdy danych NDEF nie można zmapować na typ MIME lub identyfikator URI albo gdy tag NFC nie zawiera na początku danych NDEF. W takich przypadkach obiekt Tag z informacjami o technologiach tagu i ładunku jest umieszczany w intencji ACTION_TECH_DISCOVERED.

Tabela 1 zawiera informacje o tym, jak system wysyłania tagów mapuje pola TNF i typów na typy MIME lub identyfikatory URI. Opisuje też, których plików TNF nie można zmapować na typ MIME lub identyfikator URI. W takich przypadkach system wysyłania tagów może wrócić do ACTION_TECH_DISCOVERED.

Jeśli na przykład system wysyłania tagów napotka rekord typu TNF_ABSOLUTE_URI, zmapuje pole typu zmiennej długości tego rekordu na identyfikator URI. System wysyłania tagów umieszcza ten identyfikator URI w polu danych intencji ACTION_NDEF_DISCOVERED wraz z innymi informacjami o tagu, takimi jak ładunek. Z drugiej strony, jeśli trafi na rekord typu TNF_UNKNOWN, tworzy intencję, która obejmuje zamiast tego technologie tagu.

Tabela 1. Obsługiwane pliki TNF i ich mapowania

Format nazwy typu (TNF) Mapowanie
TNF_ABSOLUTE_URI Identyfikator URI zależnie od pola typu.
TNF_EMPTY Zmniejsza się do ACTION_TECH_DISCOVERED.
TNF_EXTERNAL_TYPE Identyfikator URI oparty na identyfikatorze URN w polu typu. Identyfikator URN jest kodowany w polu typu NDEF w skróconej formie: <domain_name>:<service_name>. Android mapuje to na identyfikator URI w formularzu: vnd.android.nfc://ext/<domain_name>:<service_name>.
TNF_MIME_MEDIA Typ MIME w zależności od pola typu.
TNF_UNCHANGED Nieprawidłowy w pierwszym rekordzie, dlatego korzysta z ACTION_TECH_DISCOVERED.
TNF_UNKNOWN Zmniejsza się do ACTION_TECH_DISCOVERED.
TNF_WELL_KNOWN Typ MIME lub identyfikator URI zależą od definicji typu rekordu (RTD) ustawionej w polu typu. Więcej informacji o dostępnych komunikatach RTD i ich mapowaniach znajdziesz w tabeli 2.

Tabela 2. Obsługiwane RTD i ich mapowania dla TNF_WELL_KNOWN

Definicja typu rekordu (RTD) Mapowanie
RTD_ALTERNATIVE_CARRIER Zmniejsza się do ACTION_TECH_DISCOVERED.
RTD_HANDOVER_CARRIER Zmniejsza się do ACTION_TECH_DISCOVERED.
RTD_HANDOVER_REQUEST Zmniejsza się do ACTION_TECH_DISCOVERED.
RTD_HANDOVER_SELECT Zmniejsza się do ACTION_TECH_DISCOVERED.
RTD_SMART_POSTER Identyfikator URI na podstawie analizy ładunku.
RTD_TEXT Typ MIME pliku text/plain.
RTD_URI Identyfikator URI oparty na ładunku.

Jak tagi NFC są wysyłane do aplikacji

Gdy system wysyłania tagów utworzy intencję obejmującą tag NFC i jego informacje identyfikujące, wysyła intencję do zainteresowanej aplikacji, która filtruje intencję. Jeśli więcej niż 1 aplikacja może obsłużyć intencję, wyświetlany jest selektor aktywności, aby użytkownik mógł wybrać aktywność. System wysyłania tagów definiuje 3 intencje, które są wymienione w kolejności od najwyższego do najniższego priorytetu:

  1. ACTION_NDEF_DISCOVERED: ta intencja służy do uruchamiania działania, gdy tag zawierający ładunek NDEF jest skanowany i ma rozpoznawany typ. Jest to intencja o najwyższym priorytecie, a system wysyłania tagów próbuje uruchamiać działanie z tą intencją w miarę możliwości przed jakąkolwiek inną intencją.
  2. ACTION_TECH_DISCOVERED: jeśli nie są rejestrowane żadne działania obsługujące intencję ACTION_NDEF_DISCOVERED, system wysyłania tagów próbuje uruchomić aplikację z tą intencją. Intencje są też uruchamiane bezpośrednio (nie zaczynając od ACTION_NDEF_DISCOVERED), jeśli skanowany tag zawiera dane NDEF, których nie można zmapować na typ MIME ani identyfikator URI, lub jeśli tag nie zawiera danych NDEF, ale korzysta ze znanej technologii tagów.
  3. ACTION_TAG_DISCOVERED: ta intencja jest uruchamiana, jeśli żadna aktywność nie obsługuje intencji ACTION_NDEF_DISCOVERED lub ACTION_TECH_DISCOVERED.

Podstawowy sposób działania systemu wysyłania tagów wygląda tak:

  1. Spróbuj rozpocząć działanie z intencją utworzoną przez system wysyłania tagów podczas analizowania tagu NFC (ACTION_NDEF_DISCOVERED lub ACTION_TECH_DISCOVERED).
  2. Jeśli żadne aktywności nie są filtrowane pod kątem tej intencji, spróbuj uruchomić działanie o kolejnej intencji o najniższym priorytecie (ACTION_TECH_DISCOVERED lub ACTION_TAG_DISCOVERED), dopóki aplikacja nie przefiltruje intencji lub dopóki system wysyłania tagów nie wypróbuje wszystkich możliwych intencji.
  3. Jeśli żadna aplikacja nie filtruje żadnej intencji, nie rób nic.
Rysunek 1. System wysyłania tagów

W miarę możliwości korzystaj z wiadomości NDEF i intencji ACTION_NDEF_DISCOVERED, ponieważ jest to najbardziej precyzyjna z tych 3 komunikacji. Dzięki temu możesz uruchomić aplikację w odpowiednim momencie niż pozostałe 2 zamiary, co zapewni użytkownikowi lepsze wrażenia.

Poproś o dostęp do NFC w pliku manifestu Androida

Aby uzyskać dostęp do modułu NFC urządzenia i prawidłowo obsługiwać intencje NFC, w pliku AndroidManifest.xml zadeklaruj te elementy:

  • Element NFC <uses-permission> umożliwiający dostęp do sprzętu NFC:
    <uses-permission android:name="android.permission.NFC" />
    
  • Minimalna wersja pakietu SDK, którą może obsługiwać Twoja aplikacja. Poziom API 9 obsługuje tylko ograniczone wysyłanie tagów przez ACTION_TAG_DISCOVERED i daje dostęp do wiadomości NDEF tylko za pomocą dodatku EXTRA_NDEF_MESSAGES. Nie są dostępne żadne inne właściwości tagów ani operacje wejścia-wyjścia. Poziom 10 interfejsu API obejmuje kompleksową obsługę czytnika i zapisu, a także przekazywanie na pierwszym planie funkcji push NDEF. Poziom 14 interfejsu API zapewnia łatwiejszy sposób przesyłania komunikatów NDEF na inne urządzenia za pomocą Android Beam oraz dodatkowe wygodne metody tworzenia rekordów NDEF.
    <uses-sdk android:minSdkVersion="10"/>
    
  • Element uses-feature, dzięki któremu aplikacja pojawia się w Google Play tylko na urządzeniach wyposażonych w sprzęt NFC:
    <uses-feature android:name="android.hardware.nfc" android:required="true" />
    

    Jeśli Twoja aplikacja korzysta z komunikacji NFC, ale jej działanie nie jest niezbędne, możesz pominąć element uses-feature i sprawdzić dostępność NFC w czasie działania, sprawdzając, czy getDefaultAdapter() ma wartość null.

Filtruj według intencji NFC

Aby uruchomić aplikację po zeskanowaniu tagu NFC, który ma być obsługiwany, może ona filtrować jedną, dwie lub wszystkie trzy intencje NFC w pliku manifestu Androida. Zwykle jednak warto odfiltrować intencję ACTION_NDEF_DISCOVERED, aby uzyskać największą kontrolę nad czasem uruchamiania aplikacji. Intencja ACTION_TECH_DISCOVERED jest zastępczą intencją ACTION_NDEF_DISCOVERED, gdy nie ma filtra aplikacji dla ACTION_NDEF_DISCOVERED lub gdy ładunek nie jest typu NDEF. Filtrowanie według kategorii ACTION_TAG_DISCOVERED jest zwykle zbyt ogólne, aby można było filtrować według niego kategorię. Przed ACTION_TAG_DISCOVERED wiele aplikacji filtruje dane pod kątem ACTION_NDEF_DISCOVERED lub ACTION_TECH_DISCOVERED, więc prawdopodobieństwo uruchomienia aplikacji jest niskie. Funkcja ACTION_TAG_DISCOVERED jest dostępna w ostateczności tylko wtedy, gdy nie są zainstalowane żadne inne aplikacje obsługujące intencję ACTION_NDEF_DISCOVERED ani ACTION_TECH_DISCOVERED.

Nie zawsze jest to możliwe, dlatego w razie potrzeby możesz skorzystać z dwóch innych intencji, ponieważ wdrożenia tagów NFC są różne i często nie masz nad nimi kontroli. Jeśli masz kontrolę nad typami zapisywanych tagów i danych, zalecamy ich formatowanie za pomocą NDEF. W sekcjach poniżej dowiesz się, jak filtrować według poszczególnych typów intencji.

ACTION_NDEF_DISCOVERED

Aby filtrować intencje ACTION_NDEF_DISCOVERED, zadeklaruj filtr intencji wraz z typem danych, według których chcesz filtrować. Oto przykładowe filtry dla intencji ACTION_NDEF_DISCOVERED z typem 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>

Poniżej znajduje się przykład filtra identyfikatora URI w postaci 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

Jeśli Twoja aktywność filtruje intencję ACTION_TECH_DISCOVERED, musisz utworzyć plik zasobów XML określający technologie obsługiwane przez Twoją aktywność w zestawie tech-list. Twoja aktywność jest uznawana za dopasowanie, jeśli zbiór tech-list należy do podzbioru technologii obsługiwanych przez tag, które można uzyskać, wywołując metodę getTechList().

Jeśli na przykład skanowany tag obsługuje MifareClassic, NdefFormatable i NfcA, zbiór tech-list musi określać wszystkie 3, 2 lub jedną z nich (i niczego więcej), aby aktywność była dopasowywana.

Poniższy przykład zawiera wszystkie technologie. Musisz usunąć te, które nie są obsługiwane przez Twój tag NFC. Zapisz ten plik (możesz mu nadać dowolną nazwę) w folderze <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>

Możesz też określić wiele zestawów tech-list. Każdy zbiór tech-list jest rozpatrywany niezależnie, a Twoja aktywność jest uznawana za pasującą, jeśli dowolny zbiór tech-list jest podzbiorem technologii zwracanych przez funkcję getTechList(). Zapewnia to semantykę AND i OR technologii dopasowywania. Poniższy przykład pasuje do tagów, które obsługują technologie NfcA i Ndef lub mogące obsługiwać technologie NfcB i 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>

W pliku AndroidManifest.xml wskaż utworzony przed chwilą plik zasobów w elemencie <meta-data> w elemencie <activity>, jak w tym przykładzie:

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

Więcej informacji o korzystaniu z technologii tagów i intencji ACTION_TECH_DISCOVERED znajdziesz w sekcji Praca z obsługiwanymi technologiami tagów w dokumencie Zaawansowane NFC.

ACTION_TAG_DISCOVERED

Aby filtrować według kryterium ACTION_TAG_DISCOVERED, użyj tego filtra intencji:

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

Uzyskiwanie informacji z intencji

Jeśli działanie jest związane z intencją NFC, możesz uzyskać z niej informacje o zeskanowanym tagu NFC. W zależności od zeskanowanego tagu intencje mogą zawierać te dodatki:

Aby uzyskać te dodatki, sprawdź, czy Twoja aktywność została uruchomiona za pomocą jednej z intencji NFC i czy tag został zeskanowany, a następnie pobierz dodatkowe elementy z intencji. W tym przykładzie sprawdzamy intencję ACTION_NDEF_DISCOVERED i pobieramy komunikaty NDEF z dodatkowego intencji.

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

Możesz też uzyskać z intencji obiekt Tag, który będzie zawierać ładunek i umożliwi wyliczenie technologii tagu:

Kotlin

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

Java

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

Tworzenie popularnych typów rekordów NDEF

W tej sekcji opisujemy, jak tworzyć typowe typy rekordów NDEF, które pomogą Ci podczas zapisywania do tagów NFC lub wysyłania danych za pomocą Android Beam. Od Androida 4.0 (poziom interfejsu API 14) dostępna jest metoda createUri(), która pomaga automatycznie tworzyć rekordy URI. Od Androida 4.1 (poziom interfejsu API 16) createExternal() i createMime() ułatwiają tworzenie rekordów MIME i typów zewnętrznych NDEF. W miarę możliwości używaj tych metod pomocniczych, aby uniknąć błędów podczas ręcznego tworzenia rekordów NDEF.

W tej sekcji opisujemy też, jak utworzyć odpowiedni filtr intencji dla rekordu. Wszystkie te przykłady rekordów NDEF powinny znajdować się w pierwszym rekordzie NDEF wiadomości NDEF, którą piszesz do tagu lub przesyłania.

TNF_ABSOLUTE_Identyfikator URI

Uwaga: zalecamy używanie typu RTD_URI zamiast TNF_ABSOLUTE_URI, ponieważ jest on skuteczniejszy.

Rekord NDEF TNF_ABSOLUTE_URI możesz utworzyć w ten sposób:

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]);

Filtr intencji dla poprzedniego rekordu NDEF wyglądałby tak:

<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

Rekord NDEF TNF_MIME_MEDIA możesz utworzyć na te sposoby:

Przy użyciu metody 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")));

Ręczne utworzenie 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")));

Filtr intencji dla poprzedniego rekordu NDEF wyglądałby tak:

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

Rekord NDEF TNF_WELL_KNOWN możesz utworzyć w ten sposób:

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

Filtr intencji dla poprzedniego rekordu NDEF wyglądałby tak:

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

Rekord NDEF TNF_WELL_KNOWN możesz utworzyć na te sposoby:

Przy użyciu metody createUri(String):

Kotlin

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

Java

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

Przy użyciu metody 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);

Ręczne utworzenie 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);

Filtr intencji dla poprzedniego rekordu NDEF wyglądałby tak:

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

Rekord NDEF TNF_EXTERNAL_TYPE możesz utworzyć w następujący sposób:

Przy użyciu metody 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);

Ręczne utworzenie 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);

Filtr intencji dla poprzedniego rekordu NDEF wyglądałby tak:

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

W przypadku bardziej ogólnych wdrożeń tagów NFC użyj TNF_EXTERNAL_TYPE. Pozwoli to lepiej obsługiwać urządzenia z Androidem i urządzenia z systemem innym niż Android.

Uwaga: identyfikatory URN dla TNF_EXTERNAL_TYPE mają kanoniczny format: urn:nfc:ext:example.com:externalType, ale specyfikacja RTD Forum NFC wskazuje, że w rekordzie NDEF należy pominąć część urn:nfc:ext: numeru URN. Wystarczy więc podać domenę (w tym przykładzie example.com) i jej typ (w przykładzie externalType) oddzielone dwukropkiem. Podczas wysyłania TNF_EXTERNAL_TYPE Android konwertuje identyfikator URN urn:nfc:ext:example.com:externalType na identyfikator URI vnd.android.nfc://ext/example.com:externalType, który deklaruje filtr intencji w przykładzie.

Rekordy aplikacji na Androida

Wprowadzenie w Androidzie 4.0 (poziom interfejsu API 14) funkcji Android Application Record (AAR) zapewnia większą pewność, że aplikacja jest uruchamiana podczas skanowania tagu NFC. W AAR nazwa pakietu aplikacji umieszczona w rekordzie NDEF. Możesz dodać AAR do dowolnego rekordu NDEF wiadomości NDEF, ponieważ Android przeszukuje całą wiadomość NDEF pod kątem AAR. Gdy znajdzie narzędzie AAR, uruchomi aplikację na podstawie nazwy pakietu zawartej w nim. Jeśli na urządzeniu nie ma aplikacji, uruchamia się Google Play, by ją pobrać.

AAR przydają się, gdy chcesz uniemożliwić innym aplikacjom filtrowanie na podstawie tej samej intencji i potencjalną obsługę konkretnych wdrożonych tagów. AAR są obsługiwane tylko na poziomie aplikacji ze względu na ograniczenie nazwy pakietu, a nie na poziomie aktywności, jak w przypadku filtrowania intencji. Jeśli chcesz obsługiwać intencję na poziomie działania, użyj filtrów intencji.

Jeśli tag zawiera AAR, system jego wysyłania wysyła je w ten sposób:

  1. Spróbuj w normalny sposób uruchomić działanie, używając filtra intencji. Jeśli aktywność pasująca do intencji odpowiada też AAR, rozpocznij działanie.
  2. Jeśli aktywność filtrująca intencję nie pasuje do AAR, jeżeli wiele działań może obsłużyć intencję lub jeśli żadna aktywność nie obsługuje intencji, uruchom aplikację określoną przez AAR.
  3. Jeśli żadna aplikacja nie może skorzystać z AAR, przejdź do Google Play, aby ją pobrać.

Uwaga: możesz zastąpić AAR i system wysyłania intencji za pomocą systemu wysyłania na pierwszym planie, który sprawia, że działania na pierwszym planie mają priorytet po wykryciu tagu NFC. W przypadku tej metody działanie musi być na pierwszym planie, aby zastąpić AAR i system wysyłania intencji.

Jeśli nadal chcesz filtrować zeskanowane tagi, które nie zawierają AAR, możesz w zwykły sposób zadeklarować filtry intencji. Jest to przydatne, gdy Twoja aplikacja jest zainteresowana innymi tagami, które nie zawierają AAR. Możesz na przykład zagwarantować, że aplikacja będzie obsługiwać zarówno zastrzeżone przez Ciebie tagi, jak i tagi ogólne wdrożone przez firmy zewnętrzne. Pamiętaj, że AAR odnoszą się tylko do urządzeń z Androidem 4.0 lub nowszym, więc podczas wdrażania tagów warto użyć kombinacji typów AAR i typów MIME oraz identyfikatorów URI, aby zapewnić obsługę najszerszej gamy urządzeń. Poza tym wdrażając tagi NFC, zastanów się, jak chcesz je zapisywać, aby umożliwić obsługę większości urządzeń (z Androidem i innych). Możesz to zrobić, definiując względnie unikalny typ MIME lub identyfikator URI, aby ułatwić aplikacjom rozróżnianie.

Android udostępnia prosty interfejs API do tworzenia AAR: createApplicationRecord(). Wystarczy, że umieścisz AAR w dowolnym miejscu w pliku NdefMessage. Nie chcesz używać pierwszego rekordu NdefMessage, chyba że AAR jest jedynym rekordem w NdefMessage. Dzieje się tak, ponieważ system Android sprawdza pierwszy rekord NdefMessage, aby określić typ MIME lub identyfikator URI tagu, który służy do tworzenia intencji dla aplikacji do filtrowania. Poniższy kod pokazuje, jak utworzyć 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")}
        );
)

Przesyłaj wiadomości NDEF do innych urządzeń

Android Beam umożliwia prostą wymianę danych typu peer-to-peer między dwoma urządzeniami z systemem Android. Aplikacja, która chce przesyłać dane do innego urządzenia, musi działać na pierwszym planie, a urządzenie odbierające dane nie może być zablokowane. Gdy urządzenie wysyłające obraz znajdzie się wystarczająco blisko urządzenia odbierającego, wyświetli się interfejs „Dotknij, by przesłać”. Użytkownik może następnie zdecydować, czy wysłać wiadomość na urządzenie odbierające.

Uwaga: przekazywanie NDEF na pierwszym planie było dostępne na poziomie API 10, który zapewnia podobne funkcje co Android Beam. Te interfejsy API zostały wycofane, ale obsługują starsze urządzenia. Aby dowiedzieć się więcej, przeczytaj enableForegroundNdefPush().

Możesz włączyć Android Beam dla swojej aplikacji, wywołując jedną z dwóch metod:

  • setNdefPushMessage(): akceptuje NdefMessage, które można ustawić jako wiadomość do przesłania. Automatycznie przesyła wiadomość zbliżeniową, gdy dwa urządzenia znajdują się wystarczająco blisko.
  • setNdefPushMessageCallback(): przyjmuje wywołanie zwrotne zawierające parametr createNdefMessage(), które jest wywoływane, gdy urządzenie znajduje się w zasięgu, na które ma przesłać dane. Wywołanie zwrotne umożliwia utworzenie wiadomości NDEF tylko wtedy, gdy jest to konieczne.

Aktywność może przekazać tylko 1 wiadomość NDEF naraz, więc setNdefPushMessageCallback() ma pierwszeństwo przed setNdefPushMessage(), jeśli obie są skonfigurowane. Aby korzystać z Android Beam, trzeba przestrzegać tych ogólnych wskazówek:

  • Działanie, które przesyła dane, musi być na pierwszym planie. Oba urządzenia muszą mieć odblokowane ekrany.
  • Dane, które przesyłasz, musisz opakować w obiekcie NdefMessage.
  • Urządzenie NFC, które odbiera przesyłane dane, musi obsługiwać com.android.npp protokół NDEF push lub protokół SNEP (Simple NDEF Exchange Protocol) Forum NFC. Protokół com.android.npp jest wymagany na urządzeniach korzystających z interfejsów API na poziomie 9 (Android 2.3) i API 13 (Android 3.2). com.android.npp i SNEP są wymagane w przypadku interfejsu API na poziomie 14 (Android 4.0) i nowszych.

Uwaga: jeśli Twoja aktywność włącza Android Beam i działa na pierwszym planie, standardowy system wysyłania intencji jest wyłączony. Jeśli jednak Twoja aktywność umożliwia też wysyłanie na pierwszym planie, może nadal skanować tagi zgodne z filtrami intencji ustawionymi w wysyłaniu na pierwszym planie.

Aby włączyć Android Beam:

  1. Utwórz NdefMessage zawierający elementy NdefRecord, które chcesz przekazać na drugie urządzenie.
  2. Wywołaj setNdefPushMessage() z NdefMessage lub wywołaj setNdefPushMessageCallback, przekazując obiekt NfcAdapter.CreateNdefMessageCallback w metodzie onCreate() Twojej aktywności. Te metody wymagają co najmniej jednej aktywności, którą chcesz włączyć za pomocą Android Beam, oraz opcjonalnej listy innych działań, które chcesz włączyć.

    Zazwyczaj używasz setNdefPushMessage(), jeśli Twoja aktywność musi przez cały czas przekazywać tylko ten sam komunikat NDEF, gdy 2 urządzenia są w zasięgu, aby się komunikować. Używasz setNdefPushMessageCallback, gdy aplikacja dba o bieżący kontekst aplikacji i chce przekazać komunikat NDEF w zależności od tego, co użytkownik robi w aplikacji.

Poniższy przykład pokazuje, jak prosta aktywność wywołuje NfcAdapter.CreateNdefMessageCallback w metodzie onCreate() aktywności (pełny przykład znajdziesz na AndroidBeamDemo). W tym przykładzie znajdziesz też metody tworzenia rekordu 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()));
    }
}

Pamiętaj, że ten kod komentuje AAR, którą możesz usunąć. Jeśli włączysz AAR, aplikacja określona w nim zawsze będzie otrzymywać wiadomość Android Beam. Gdy aplikacja nie jest widoczna, Google Play zaczyna ją pobierać. Dlatego w przypadku urządzeń z Androidem 4.0 lub nowszym, jeśli używasz AAR, technicznie nie musisz używać tego filtra intencji:

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

Dzięki temu filtrowi intencji aplikację com.example.android.beam można teraz uruchamiać, gdy skanuje tag NFC lub odbiera Android Beam z AAR typu com.example.android.beam albo gdy wiadomość w formacie NDEF zawiera rekord MIME typu application/vnd.com.example.android.beam.

Mimo że AAR gwarantuje uruchomienie lub pobranie aplikacji, filtry intencji są zalecane, ponieważ pozwalają uruchamiać wybrane działanie w aplikacji, zamiast uruchamiać główne działanie w pakiecie określonym przez AAR. AAR nie mają szczegółowości na poziomie aktywności. Poza tym niektóre urządzenia z Androidem nie obsługują AAR, dlatego na wszelki wypadek umieść informacje identyfikacyjne w pierwszym rekordzie NDEF wiadomości NDEF i odpowiednio ustaw filtr. Więcej informacji o tworzeniu rekordów znajdziesz w artykule Tworzenie popularnych typów rekordów NDEF.