Panoramica avanzata di NFC

Questo documento descrive argomenti NFC avanzati, ad esempio l'utilizzo di varie tecnologie di tag, la scrittura in tag NFC e l'invio in primo piano, che consente a un'applicazione in primo piano di gestire gli intent anche quando altre applicazioni filtrano gli stessi.

Utilizzo delle tecnologie di tag supportate

Quando si utilizzano tag NFC e dispositivi Android, il formato principale utilizzato per leggere e scrivere dati sui tag è NDEF. Quando un dispositivo analizza un tag con dati NDEF, Android supporta l'analisi del messaggio e la relativa consegna in un NdefMessage, se possibile. Tuttavia, esistono casi in cui esegui la scansione di un tag che non contiene dati NDEF o non è stato possibile mappare i dati NDEF a un tipo o URI MIME. In questi casi, devi aprire la comunicazione direttamente con il tag e leggere e scrivere usando il tuo protocollo (in byte non elaborati). Android fornisce un supporto generico per questi casi d'uso con il pacchetto android.nfc.tech, descritto nella Tabella 1. Puoi utilizzare il metodo getTechList() per determinare le tecnologie supportate dal tag e creare l'oggetto TagTechnology corrispondente con una delle classi fornite da android.nfc.tech

Tabella 1. Tecnologie di tag supportate

Classe Descrizione
TagTechnology L'interfaccia che tutte le classi di tecnologia tag devono implementare.
NfcA Consente di accedere alle proprietà NFC-A (ISO 14443-3A) e alle operazioni di I/O.
NfcB Consente di accedere alle proprietà NFC-B (ISO 14443-3B) e alle operazioni di I/O.
NfcF Consente di accedere alle proprietà NFC-F (JIS 6319-4) e alle operazioni di I/O.
NfcV Consente di accedere alle proprietà NFC-V (ISO 15693) e alle operazioni di I/O.
IsoDep Consente di accedere alle proprietà ISO-DEP (ISO 14443-4) e alle operazioni di I/O.
Ndef Consente di accedere ai dati NDEF e alle operazioni sui tag NFC formattati come NDEF.
NdefFormatable Fornisce operazioni di formattazione per i tag che possono essere formattabili NDEF.

I dispositivi con piattaforma Android non richiedono il supporto delle seguenti tecnologie di tag.

Tabella 2. Tecnologie di tag supportate facoltative

Classe Descrizione
MifareClassic Fornisce l'accesso alle proprietà MIFARE Classic e alle operazioni di I/O, se questo dispositivo Android supporta MIFARE.
MifareUltralight Consente di accedere alle proprietà MIFARE Ultralight e alle operazioni di I/O, se questo dispositivo Android supporta MIFARE.

Utilizzare le tecnologie di tag e l'intent ACTION_TECH_DISCOVERED

Quando un dispositivo analizza un tag contenente dati NDEF, ma non è stato possibile mappare a un MIME o un URI, il sistema di invio dei tag tenta di avviare un'attività con l'intent ACTION_TECH_DISCOVERED. ACTION_TECH_DISCOVERED viene utilizzato anche quando viene scansionato un tag con dati non NDEF. Questo tipo di riserva ti consente di lavorare direttamente con i dati nel tag se il sistema di invio dei tag non è riuscito ad analizzarlo per te. I passaggi di base per l'utilizzo delle tecnologie di tag sono i seguenti:

  1. Filtra per un intent ACTION_TECH_DISCOVERED che specifica le tecnologie di tag che vuoi gestire. Per ulteriori informazioni, consulta Filtro per intent NFC. In generale, il sistema di invio dei tag tenta di avviare un intent ACTION_TECH_DISCOVERED quando un messaggio NDEF non può essere mappato a un tipo o URI MIME o se il tag analizzato non conteneva dati NDEF. Per ulteriori informazioni su come questo viene determinato, consulta la sezione Il sistema di invio dei tag.
  2. Quando l'applicazione riceve l'intent, recupera l'oggetto Tag dall'intent:

    Kotlin

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

    Java

    Tag tagFromIntent = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
    
  3. Per ottenere un'istanza di TagTechnology, chiama uno dei metodi di fabbrica get delle classi nel pacchetto android.nfc.tech. Puoi enumerare le tecnologie supportate del tag chiamando getTechList() prima di chiamare un metodo di fabbrica get. Ad esempio, per ottenere un'istanza di MifareUltralight da un Tag, procedi nel seguente modo:

    Kotlin

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

    Java

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

Lettura e scrittura nei tag

La lettura e la scrittura in un tag NFC implica l'ottenimento del tag dall'intent e l'apertura della comunicazione con il tag. Devi definire uno stack di protocollo personalizzato per leggere e scrivere dati nel tag. Tuttavia, tieni presente che puoi comunque leggere e scrivere dati NDEF quando lavori direttamente con un tag. Spetta a te decidere come strutturare le cose. L'esempio riportato di seguito mostra come utilizzare un tag 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;
    }
}

Utilizza il sistema di tracciamento in primo piano

Il sistema di invio in primo piano consente a un'attività di intercettare un intent e rivendicare la priorità rispetto ad altre attività che gestiscono lo stesso intento. L'utilizzo di questo sistema comporta la creazione di alcune strutture di dati affinché il sistema Android possa inviare gli intent appropriati alla tua applicazione. Per attivare il sistema di tracciamento in primo piano:

  1. Aggiungi il seguente codice nel metodo onCreate() della tua attività:
    1. Crea un oggetto PendingIntent modificabile in modo che il sistema Android possa completarlo con i dettagli del tag quando viene scansionato.

      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. Dichiara i filtri per intent per gestire gli intent che vuoi intercettare. Il sistema di invio in primo piano controlla i filtri per intent specificati con l'intent ricevuto quando il dispositivo analizza un tag. Se corrisponde, l'applicazione gestisce l'intent. Se non corrisponde, il sistema di invio in primo piano utilizza il sistema di invio degli intent. Se specifichi un array null di filtri per intent e filtri tecnologici, specifichi che vuoi filtrare tutti i tag di riserva all'intent TAG_DISCOVERED. Lo snippet di codice riportato di seguito gestisce tutti i tipi MIME per NDEF_DISCOVERED. Dovresti gestire solo quelli di cui hai bisogno.

      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. Configura una serie di tecnologie di tag che l'applicazione vuole gestire. Chiama il metodo Object.class.getName() per ottenere la classe della tecnologia che vuoi supportare.

      Kotlin

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

      Java

      techListsArray = new String[][] { new String[] { NfcF.class.getName() } };
      
  2. Esegui l'override dei seguenti callback del ciclo di vita delle attività e aggiungi la logica per abilitare e disabilitare l'invio in primo piano quando l'attività perde (onPause()) e riprende lo stato attivo (onResume()). enableForegroundDispatch() deve essere chiamato dal thread principale e solo quando l'attività è in primo piano (la chiamata in onResume() lo garantisce). Devi inoltre implementare il callback onNewIntent per elaborare i dati provenienti dal tag NFC scansionato.
  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
    }