En este documento, se describen temas avanzados de NFC, como el trabajo con varias tecnologías de etiquetas, la escritura en etiquetas NFC y el despacho en primer plano, lo que permite que una aplicación en primer plano maneje intents 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 compatibilidad 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 los datos NDEF no se pudieron asignar a un tipo MIME o URI.
En estos casos, debes abrir la comunicación directamente con la etiqueta y leerla y escribirla con tu propio protocolo (en bytes sin procesar). Android proporciona compatibilidad genérica para estos casos de uso 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
.
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 las siguientes tecnologías de etiquetas sean compatibles con los dispositivos con tecnología Android.
Clase | Descripción |
---|---|
MifareClassic |
Proporciona acceso a las propiedades y las 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 pudo 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 resguardo te permite trabajar directamente con los datos de la etiqueta si el sistema de envío de etiquetas no pudo analizarlo por ti. Los pasos básicos para trabajar con tecnologías de etiquetas son los siguientes:
- Filtra por un intent
ACTION_TECH_DISCOVERED
y especifica las tecnologías de etiqueta que deseas controlar. Consulta Filtrado de intents de NFC para obtener más información. En general, el sistema de envío de etiquetas intenta iniciar un intentACTION_TECH_DISCOVERED
cuando un mensaje NDEF no se puede asignar 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
. Para enumerar las tecnologías compatibles de la etiqueta, llama 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 una etiqueta NFC y escribir en ella implica obtener la etiqueta del intent y abrir la comunicación con ella. Debes 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. En el siguiente ejemplo, se 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 sobre 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 adecuados a tu aplicación. Para habilitar el sistema de envío en primer plano, haz lo siguiente:
- Agrega el siguiente código al método
onCreate()
de tu actividad:- Crea un objeto
PendingIntent
mutable para que el sistema Android pueda propagarlo con los detalles de la etiqueta cuando se analice.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);
- Declara filtros de intent para manejar los intents que deseas interceptar. El sistema de envío en primer plano 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. De lo contrario, el sistema de envío en primer plano recurre al sistema de envío del intent.
Al especificar un array
null
de filtros de intents y 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 necesitas.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 quieres 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 pierda (
onPause()
) y recupere (onResume()
) el enfoque. Se debe llamar aenableForegroundDispatch()
desde el subproceso 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 }