Descripción general de conceptos avanzados de NFC

En este documento, se describen temas avanzados de NFC (por ejemplo, el trabajo con varias tecnologías de etiquetas), la escritura en etiquetas NFC y el envío en primer plano, lo que permite que una aplicación en primer plano gestionar intents incluso cuando otras aplicaciones filtren por los mismos.

Cómo trabajar con tecnologías de etiquetas compatibles

Cuando trabajas con etiquetas NFC y dispositivos con Android, el formato principal que usas para leer y escribir datos en las etiquetas es NDEF. Cuando un dispositivo escanea una etiqueta con datos NDEF, Android proporciona asistencia. analizar el mensaje y entregarlo en un NdefMessage cuando como sea posible. Sin embargo, hay casos en los que se escanea una etiqueta que no contiene Datos NDEF o cuando los datos NDEF no pudieron asignarse a un tipo MIME o URI. En estos casos, debes abrir la comunicación directamente con la etiqueta y leerla y escribir en ella con tu propio protocolo (en bytes sin procesar). Android ofrece compatibilidad genérica para estos casos de uso con el android.nfc.tech, que se describe en la Tabla 1 Puedes usar el método getTechList() para determinar las tecnologías compatibles con la etiqueta y crea el TagTechnology correspondiente Un objeto con una de las clases proporcionadas por android.nfc.tech

Tabla 1: Tecnologías de etiquetas compatibles

Clase Descripción
TagTechnology La interfaz que deben implementar todas las clases de tecnología de etiquetas.
NfcA Proporciona acceso a propiedades de NFC-A (ISO 14443-3A) y a las operaciones de E/S.
NfcB Proporciona acceso a las propiedades NFC-B (ISO 14443-3B) y a las operaciones de E/S.
NfcF Proporciona acceso a las propiedades NFC-F (JIS 6319-4) y a las operaciones de E/S.
NfcV Proporciona acceso a las propiedades NFC-V (ISO 15693) y a las operaciones de E/S.
IsoDep Proporciona acceso a las propiedades ISO-DEP (ISO 14443-4) y a las operaciones de E/S.
Ndef Proporciona acceso a datos y operaciones NDEF en etiquetas NFC que han sido formateadas como NDEF
NdefFormatable Proporciona operaciones de formato para etiquetas que pueden tener formato NDEF.

No es necesario que los dispositivos con Android sean compatibles con las siguientes tecnologías de etiquetas.

Tabla 2: Tecnologías de etiquetas compatibles opcionales

Clase Descripción
MifareClassic Proporciona acceso a las propiedades de MIFARE Classic y a las operaciones de E/S si este dispositivo Android admite MIFARE.
MifareUltralight Proporciona acceso a las propiedades de MIFARE Ultralight y a las operaciones de E/S si este dispositivo es compatible con MIFARE.

Cómo trabajar con tecnologías de etiquetas y el intent ACTION_TECH_DISCOVERED

Cuando un dispositivo escanea una etiqueta que tiene datos NDEF, pero no puede asignarse a un MIME o URI, el sistema de envío de etiquetas intenta iniciar una actividad con el ACTION_TECH_DISCOVERED . El ACTION_TECH_DISCOVERED también se usa cuando una etiqueta con datos que no son NDEF. Tener este resguardo le permite trabajar con los datos en la etiqueta directamente si el sistema de envío de etiquetas no pudo analizarlo. Los pasos básicos cuando trabajas con de etiquetas son las siguientes:

  1. Filtra por un intent ACTION_TECH_DISCOVERED que especifique la tecnologías de etiquetas que quieres manejar. Consulta Filtrado para NFC intents para obtener más información. En general, el sistema de envío de etiquetas intenta iniciar un intent ACTION_TECH_DISCOVERED cuando se muestra un mensaje NDEF no se puede asignar a un tipo MIME o URI, o si la etiqueta analizada no contenía datos NDEF. Para Para obtener más información sobre cómo se determina esto, consulta El sistema de envío de etiquetas.
  2. Cuando tu aplicación reciba el intent, obtén el objeto Tag del el intent:

    Kotlin

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

    Java

    Tag tagFromIntent = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
    
  3. Obtén una instancia de TagTechnology llamando a uno de los Métodos de fábrica get de las clases en el paquete android.nfc.tech. Puedes Para enumerar las tecnologías compatibles de la etiqueta, llama a getTechList() antes de llamar a un método de fábrica get. Por ejemplo, para obtener una instancia de MifareUltralight desde un Tag, haz lo siguiente:

    Kotlin

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

    Java

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

Cómo leer y escribir en etiquetas

Leer y escribir en una etiqueta NFC implica obtener la etiqueta del intent y abriendo la comunicación con la etiqueta. Debes definir tu propia pila de protocolos para leer y escribir datos a la etiqueta. Sin embargo, ten en cuenta que aún puedes leer y escribir datos NDEF cuando trabajes directamente con una etiqueta. Tú eliges cómo estructurar las cosas. El En el siguiente ejemplo, se muestra cómo trabajar con una placa 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;
    }
}

Cómo usar el sistema de envío en primer plano

El sistema de envío en primer plano permite que una actividad intercepte un intent y una reclamación prioridad sobre otras actividades que manejan el mismo intent. El uso de este sistema implica construir algunas estructuras de datos para que el sistema Android pueda enviar la respuesta intents a tu aplicación. Para habilitar el sistema de envío en primer plano, haz lo siguiente:

  1. Agrega el siguiente código al método onCreate() de tu actividad:
    1. Crea un objeto PendingIntent mutable para que el sistema Android pueda propagarlo con los detalles de la etiqueta cuando se escanea.

      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. Declara filtros de intent para manejar los intents que deseas interceptar. El primer plano el sistema de envío verifica los filtros de intents especificados con el intent que se recibe cuando el dispositivo escanea una etiqueta. Si coincide, entonces tu aplicación maneja el intent. Si aparece no coincide, el sistema de envío en primer plano recurre al sistema de envío de intents. Cuando especificas un array null de filtros de intents y filtros de tecnología, se especifica que quieres filtrar para todas las etiquetas que recurren a TAG_DISCOVERED . El siguiente fragmento de código maneja todos los tipos MIME para NDEF_DISCOVERED. Tú solo debe manejar los que necesites.

      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 variedad de tecnologías de etiquetas que tu aplicación quiera manejar. Llama al Object.class.getName() para obtener la clase de la tecnología que usas que deseas admitir.

      Kotlin

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

      Java

      techListsArray = new String[][] { new String[] { NfcF.class.getName() } };
      
  2. Anula las siguientes devoluciones de llamada del ciclo de vida de la actividad y agrega lógica para habilitar e inhabilitar la envío en primer plano cuando la actividad pierde (onPause()) y recupera (onResume()) el enfoque. Se debe llamar a enableForegroundDispatch() desde el subproceso principal y solo cuando la actividad está en primer plano (llamar a onResume() garantiza esto). También debes implementar la devolución de llamada onNewIntent para procesar los datos del NFC escaneado. etiqueta.
  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
    }