В этом документе описываются расширенные темы NFC, такие как работа с различными технологиями тегов, запись в теги NFC и диспетчеризация приоритетного плана, которая позволяет приложению на переднем плане обрабатывать намерения, даже если другие приложения фильтруют те же самые намерения.
Работа с поддерживаемыми технологиями тегов
При работе с тегами NFC и устройствами под управлением Android основным форматом, который вы используете для чтения и записи данных в тегах, является NDEF. Когда устройство сканирует тег с данными NDEF, Android обеспечивает поддержку анализа сообщения и его доставки в NdefMessage
, если это возможно. Однако бывают случаи, когда вы сканируете тег, который не содержит данных NDEF, или когда данные 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
из намерения:Котлин
var tagFromIntent: Tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG)
Ява
Tag tagFromIntent = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
- Получите экземпляр
TagTechnology
, вызвав один из заводских методовget
классов в пакетеandroid.nfc.tech
. Вы можете перечислить поддерживаемые технологии тега, вызвавgetTechList()
перед вызовом фабричного методаget
. Например, чтобы получить экземплярMifareUltralight
изTag
, выполните следующие действия:Котлин
MifareUltralight.get(intent.getParcelableExtra(NfcAdapter.EXTRA_TAG))
Ява
MifareUltralight.get(intent.getParcelableExtra(NfcAdapter.EXTRA_TAG));
Чтение и запись в теги
Чтение и запись метки NFC предполагает получение метки из намерения и открытие связи с меткой. Вы должны определить свой собственный стек протоколов для чтения и записи данных в тег. Однако имейте в виду, что вы по-прежнему можете читать и записывать данные NDEF при работе непосредственно с тегом. Вам решать, как вы хотите структурировать вещи. В следующем примере показано, как работать с тегом MIFARE Ultralight.
Котлин
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")) } } }
Ява
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()
вашей активности:- Создайте изменяемый объект
PendingIntent
, чтобы система Android могла заполнить его сведениями о теге при его сканировании.Котлин
val intent = Intent(this, javaClass).apply { addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP) } var pendingIntent: PendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_MUTABLE)
Ява
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
. Вы должны обрабатывать только те, которые вам нужны.Котлин
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)
Ява
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()
, чтобы получить класс технологии, которую вы хотите поддерживать.Котлин
techListsArray = arrayOf(arrayOf<String>(NfcF::class.java.name))
Ява
techListsArray = new String[][] { new String[] { NfcF.class.getName() } };
- Создайте изменяемый объект
- Переопределите следующие обратные вызовы жизненного цикла активности и добавьте логику для включения и отключения отправки переднего плана, когда активность теряет (
onPause()
) и восстанавливает (onResume()
) фокус.enableForegroundDispatch()
должен вызываться из основного потока и только тогда, когда активность находится на переднем плане (вызовonResume()
гарантирует это). Вам также необходимо реализовать обратный вызовonNewIntent
для обработки данных из отсканированного тега NFC.
Котлин
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 }
Ява
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 }