В этом документе описываются расширенные темы NFC, такие как работа с различными технологиями тегов, запись в теги NFC и диспетчеризация переднего плана, которая позволяет приложению на переднем плане обрабатывать намерения, даже если другие приложения фильтруют те же самые.
Поддерживаемые технологии тегов
При работе с NFC-тегами и устройствами Android основным форматом, который вы используете для чтения и записи данных в тегах, является NDEF. Когда устройство сканирует тег с данными NDEF, Android помогает проанализировать сообщение и по возможности передать его в NdefMessage. Однако в некоторых случаях тег не содержит данных NDEF или их невозможно сопоставить с MIME-типом или URI.
В таких случаях вам нужно установить связь непосредственно с тегом и считывать и записывать данные в него с помощью собственного протокола (в необработанных байтах). Android предоставляет общую поддержку для этих сценариев использования с помощью пакета android.nfc.tech, который описан в таблице 1. Вы можете использовать метод getTechList(), чтобы определить, какие технологии поддерживает тег, и создать соответствующий объект TagTechnology с помощью одного из классов, предоставляемых android.nfc.tech.
Таблица 1. Поддерживаемые технологии тегов
| Класс | Описание |
|---|---|
TagTechnology |
Интерфейс, который должны реализовывать все классы технологий тегов. |
NfcA |
Предоставляет доступ к свойствам NFC-A (ISO 14443-3A) и операциям ввода-вывода. |
NfcB |
Предоставляет доступ к свойствам NFC-B (ISO 14443-3B) и операциям ввода-вывода. |
NfcF |
Предоставляет доступ к свойствам NFC-F (JIS 6319-4) и операциям ввода-вывода. |
NfcV |
Предоставляет доступ к свойствам NFC-V (ISO 15693) и операциям ввода-вывода. |
IsoDep |
Предоставляет доступ к свойствам ISO-DEP (ISO 14443-4) и операциям ввода-вывода. |
Ndef |
Предоставляет доступ к данным NDEF и операциям с NFC-тегами, отформатированными как NDEF. |
NdefFormatable |
Предоставляет операции форматирования для тегов, которые могут быть отформатированы в NDEF. |
Устройства Android не обязаны поддерживать следующие технологии тегов:
Таблица 2. Поддерживаемые технологии тегов (необязательно)
| Класс | Описание |
|---|---|
MifareClassic |
Предоставляет доступ к свойствам MIFARE Classic и операциям ввода-вывода, если устройство Android поддерживает MIFARE. |
MifareUltralight |
Предоставляет доступ к свойствам MIFARE Ultralight и операциям ввода-вывода, если это устройство Android поддерживает MIFARE. |
Как работать с технологиями тегов и намерением ACTION_TECH_DISCOVERED
Когда устройство сканирует тег с данными NDEF, которые не удается сопоставить с MIME или URI, система отправки тегов пытается запустить действие с намерением ACTION_TECH_DISCOVERED. ACTION_TECH_DISCOVERED также используется при сканировании тега с данными, не относящимися к NDEF. Это позволяет работать с данными тега напрямую, если система отправки тегов не смогла их обработать. Основные этапы работы с технологиями тегов:
- Фильтр для намерения
ACTION_TECH_DISCOVERED, указывающий технологии тегов, которые вы хотите обрабатывать. Подробнее о фильтрации намерений NFC… Как правило, система диспетчеризации тегов пытается запустить намерениеACTION_TECH_DISCOVERED, когда сообщение NDEF не удается сопоставить с MIME-типом или URI или если отсканированный тег не содержит данных NDEF. Подробнее о том, как это определяется, читайте в разделе Система отправки тегов. - Когда приложение получит намерение, извлеките из него объект
Tag:Kotlin
var tagFromIntent: Tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG)
Java
Tag tagFromIntent = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
- Получите экземпляр
TagTechnology, вызвав один из фабричных методовgetклассов в пакетеandroid.nfc.tech. Вы можете перечислить поддерживаемые технологии тега, вызвавgetTechList()перед вызовом фабричного методаget. Например, чтобы получить экземплярMifareUltralightизTag, выполните следующие действия:Kotlin
MifareUltralight.get(intent.getParcelableExtra(NfcAdapter.EXTRA_TAG))
Java
MifareUltralight.get(intent.getParcelableExtra(NfcAdapter.EXTRA_TAG));
Чтение и запись тегов.
Чтобы считать или записать данные на NFC-метку, нужно получить ее из намерения и установить с ней связь. Чтобы считывать данные с тега и записывать их на него, необходимо определить собственный стек протоколов. Однако вы по-прежнему можете читать и записывать данные NDEF при непосредственной работе с тегом. Вы можете структурировать данные так, как вам удобно. В примере ниже показано, как работать с тегом 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; } }
Использование системы диспетчеризации переднего плана
Система диспетчеризации переднего плана позволяет действию перехватывать намерение и заявлять о приоритете над другими действиями, которые обрабатывают то же намерение. Для использования этой системы необходимо создать несколько структур данных, чтобы система Android могла отправлять в ваше приложение соответствующие намерения. Чтобы включить систему диспетчеризации переднего плана:
- Добавьте следующий код в метод
onCreate()своего класса activity:- Создайте изменяемый объект
PendingIntent, чтобы система Android могла заполнить его данными тега при сканировании.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);
- Объявите фильтры интентов, чтобы обрабатывать интенты, которые вы хотите перехватывать. Система диспетчеризации переднего плана проверяет указанные фильтры интентов с помощью интента, полученного при сканировании тега устройством. Если совпадение есть, приложение обрабатывает намерение. Если совпадение не найдено, система диспетчеризации переднего плана возвращается к системе диспетчеризации намерений.
Указав массив фильтров интентов и технологий
null, вы задаете фильтрацию по всем тегам, которые возвращаются к интентуTAG_DISCOVERED. Приведенный ниже фрагмент кода обрабатывает все MIME-типы дляNDEF_DISCOVERED. Вам нужно обрабатывать только те, которые вам необходимы.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, };
- Настройте массив технологий тегов, которые должно обрабатывать ваше приложение. Вызовите метод
Object.class.getName(), чтобы получить класс технологии, которую вы хотите поддерживать.Kotlin
techListsArray = arrayOf(arrayOf<String>(NfcF::class.java.name))
Java
techListsArray = new String[][] { new String[] { NfcF.class.getName() } };
- Создайте изменяемый объект
- Переопределите следующие обратные вызовы жизненного цикла объекта activity и добавьте логику, чтобы включать и отключать диспетчеризацию переднего плана, когда объект activity теряет (
onPause()) и восстанавливает (onResume()) фокус. МетодenableForegroundDispatch()необходимо вызывать из основного потока и только когда действие находится на переднем плане (вызов вonResume()гарантирует это). Вам также нужно реализовать обратный вызовonNewIntent, чтобы обрабатывать данные отсканированного NFC-тега.
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 }