En este documento, se describen temas avanzados de NFC, por ejemplo, cómo trabajar con varias tecnologías de etiquetas, escribir en etiquetas NFC y despachar en primer plano, lo que permite que una aplicación en primer plano maneje intentos incluso cuando otras aplicaciones filtran 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 etiquetas es NDEF. Cuando un dispositivo escanea una etiqueta con datos NDEF, Android proporciona asistencia para analizar el mensaje y entregarlo en un NdefMessage
siempre que sea posible. Sin embargo, hay casos en los que escaneas una etiqueta que no contiene datos NDEF o cuando no se pudieron asignar los datos NDEF a un tipo MIME o URI.
En estos casos, debes abrir la comunicación directamente con la etiqueta, y leerla y escribirla con su propio protocolo (en bytes sin procesar). Android proporciona compatibilidad genérica para estos casos prácticos con el paquete 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 crear el objeto TagTechnology
correspondiente 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 y operaciones de E/S de MIFARE Classic si el dispositivo Android es compatible con MIFARE. |
MifareUltralight |
Proporciona acceso a las propiedades de MIFARE Ultralight y a las operaciones de E/S si el dispositivo Android 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 se puede asignar a un MIME o URI, el sistema de envío de etiquetas intenta iniciar una actividad con el intent ACTION_TECH_DISCOVERED
. El ACTION_TECH_DISCOVERED
también se usa cuando se escanea una etiqueta con datos que no son NDEF. Tener este respaldo te permite trabajar con los datos directamente en la etiqueta si el sistema de envío de etiquetas no puede analizarlo en tu lugar. Los pasos básicos de trabajo con tecnologías de etiquetas son los siguientes:
- Filtra por un intent
ACTION_TECH_DISCOVERED
especificando las tecnologías de etiquetas que deseas manejar. Para obtener más información, consulta Filtrado de intents de NFC. En general, el sistema de envío de etiquetas trata de iniciar un intentACTION_TECH_DISCOVERED
cuando no se puede asignar un mensaje NDEF a un tipo MIME o URI, o si la etiqueta escaneada no contenía datos NDEF. Para obtener más información sobre cómo se determina esto, consulta El sistema de envío de etiquetas. - Cuando tu aplicación recibe el intent, obtén el objeto
Tag
del intent:Kotlin
var tagFromIntent: Tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG)
Java
Tag tagFromIntent = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
- Obtén una instancia de un
TagTechnology
llamando a uno de los métodos de fábricaget
de las clases en el paqueteandroid.nfc.tech
. Puedes enumerar las tecnologías compatibles de la etiqueta llamando agetTechList()
antes de llamar a un método de fábricaget
. Por ejemplo, para obtener una instancia deMifareUltralight
desde unTag
, 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 abrir la comunicación con ella. Tienes que definir tu propia pila de protocolos para leer y escribir datos en 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 siguiente ejemplo muestra cómo trabajar con una etiqueta 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 tenga prioridad con respecto a otras actividades que manejan el mismo intent. El uso de este sistema implica la construcción de algunas estructuras de datos para que el sistema Android pueda enviar los intents apropiados a tu aplicación. Para habilitar el sistema de envío en primer plano, haz lo siguiente:
- Agrega el siguiente código en el método
onCreate()
de tu actividad:- Crea un objeto
PendingIntent
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, 0)
Java
PendingIntent pendingIntent = PendingIntent.getActivity( this, 0, new Intent(this, getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);
- Declara filtros de intent para manejar los intents que deseas interceptar. El sistema de envío en primer plano verifica los filtros de intent especificados con el intent que se recibe cuando el dispositivo escanea una etiqueta. Si coincide, entonces tu aplicación maneja el intent. De lo contrario, el sistema de envío en primer plano vuelve al sistema de envío del intent.
Al especificar una matriz
null
de filtros de intent y filtros de tecnología, se especifica que deseas filtrar todas las etiquetas que recurren al intentTAG_DISCOVERED
. El siguiente fragmento de código maneja todos los tipos MIME paraNDEF_DISCOVERED
. Solo debes administrar 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, };
- Configura una variedad de tecnologías de etiquetas que tu aplicación quiera manejar. Llama al método
Object.class.getName()
para obtener la clase de la tecnología que deseas admitir.Kotlin
techListsArray = arrayOf(arrayOf<String>(NfcF::class.java.name))
Java
techListsArray = new String[][] { new String[] { NfcF.class.getName() } };
- Crea un objeto
- Anula las siguientes devoluciones de llamada del ciclo de vida de la actividad y agrega lógica para habilitar o inhabilitar el envío en primer plano cuando la actividad pierde (
onPause()
) y recupera (onResume()
) el enfoque. Se debe invocarenableForegroundDispatch()
desde la conversación principal y solo cuando la actividad está en primer plano (llamar aonResume()
garantiza esto). También debes implementar la devolución de llamadaonNewIntent
para procesar los datos de la etiqueta NFC escaneada.
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 }
Consulta el ejemplo ForegroundDispatch de las demostraciones de la API para obtener la muestra completa.