В этом документе описаны основные задачи NFC, которые вы выполняете в Android. В нем объясняется, как отправлять и получать данные NFC в форме сообщений NDEF, и описываются API-интерфейсы платформы Android, которые поддерживают эти функции. Более сложные темы, включая обсуждение работы с данными, не относящимися к NDEF, см. в разделе Advanced NFC .
Существует два основных варианта использования при работе с данными NDEF и Android:
- Чтение данных NDEF из метки NFC
- Передача сообщений NDEF с одного устройства на другое с помощью Android Beam™
Чтение данных NDEF из тега NFC обрабатывается системой диспетчеризации тегов , которая анализирует обнаруженные теги NFC, соответствующим образом классифицирует данные и запускает приложение, заинтересованное в классифицированных данных. Приложение, которое хочет обрабатывать отсканированную метку NFC, может объявить фильтр намерений и запросить обработку данных.
Функция Android Beam™ позволяет устройству передавать сообщение NDEF на другое устройство путем физического соединения устройств друг с другом. Такое взаимодействие обеспечивает более простой способ отправки данных, чем другие беспроводные технологии, такие как Bluetooth, поскольку благодаря NFC не требуется ручное обнаружение или сопряжение устройств. Соединение автоматически запускается, когда два устройства входят в зону действия. Android Beam доступен через набор API-интерфейсов NFC, поэтому любое приложение может передавать информацию между устройствами. Например, приложения «Контакты», «Браузер» и YouTube используют Android Beam для обмена контактами, веб-страницами и видео с другими устройствами.
Система отправки тегов
Устройства на базе Android обычно ищут метки NFC, когда экран разблокирован, если только NFC не отключен в меню настроек устройства. Когда устройство под управлением Android обнаруживает метку NFC, желательно, чтобы наиболее подходящее действие обрабатывало намерение, не спрашивая пользователя, какое приложение использовать. Поскольку устройства сканируют метки NFC на очень небольшом расстоянии, вполне вероятно, что если пользователям придется выбирать действие вручную, им придется отодвинуть устройство от метки и разорвать соединение. Вам следует разработать свою деятельность так, чтобы она обрабатывала только те NFC-теги, которые важны для вашей деятельности, чтобы предотвратить появление средства выбора активности.
Чтобы помочь вам в достижении этой цели, Android предоставляет специальную систему отправки тегов, которая анализирует отсканированные теги NFC, анализирует их и пытается найти приложения, которым интересны отсканированные данные. Это делается путем:
- Анализ тега NFC и определение типа MIME или URI, который идентифицирует полезные данные в теге.
- Инкапсуляция типа MIME или URI и полезных данных в намерение. Эти первые два шага описаны в разделе «Как теги NFC сопоставляются с типами MIME и URI» .
- Запускает действие на основе намерения. Это описано в разделе «Как теги NFC отправляются в приложения» .
Как теги NFC сопоставляются с типами MIME и URI
Прежде чем приступить к написанию приложений NFC, важно понять различные типы тегов NFC, как система отправки тегов анализирует теги NFC и какую специальную работу выполняет система отправки тегов при обнаружении сообщения NDEF. Теги NFC используют широкий спектр технологий, и в них также могут быть записаны данные разными способами. Android имеет наибольшую поддержку стандарта NDEF, определенного NFC Forum .
Данные NDEF инкапсулируются внутри сообщения ( NdefMessage
), которое содержит одну или несколько записей ( NdefRecord
). Каждая запись NDEF должна иметь правильный формат в соответствии со спецификацией типа записи, которую вы хотите создать. Android также поддерживает другие типы тегов, не содержащие данных NDEF, с которыми вы можете работать, используя классы в пакете android.nfc.tech
. Дополнительные сведения об этих технологиях см. в разделе «Расширенный NFC» . Работа с другими типами тегов предполагает написание собственного стека протоколов для взаимодействия с тегами, поэтому мы рекомендуем по возможности использовать NDEF для упрощения разработки и максимальной поддержки устройств под управлением Android.
Примечание. Чтобы загрузить полные спецификации NDEF, перейдите на сайт «Спецификации и документы приложений NFC Forum» и ознакомьтесь с примерами создания записей NDEF в разделе «Создание общих типов записей NDEF».
Теперь, когда вы имеете некоторый опыт работы с тегами NFC, в следующих разделах более подробно описывается, как Android обрабатывает теги в формате NDEF. Когда устройство под управлением Android сканирует тег NFC, содержащий данные в формате NDEF, оно анализирует сообщение и пытается определить тип MIME данных или идентифицирующий URI. Для этого система считывает первую NdefRecord
внутри NdefMessage
, чтобы определить, как интерпретировать все сообщение NDEF (сообщение NDEF может иметь несколько записей NDEF). В правильно сформированном сообщении NDEF первая NdefRecord
содержит следующие поля:
- 3-битный TNF (формат имени типа)
- Указывает, как интерпретировать поле типа переменной длины. Допустимые значения описаны в Таблице 1 .
- Тип переменной длины
- Описывает тип записи. При использовании
TNF_WELL_KNOWN
используйте это поле для указания определения типа записи (RTD). Действительные значения RTD описаны в Таблице 2 . - Идентификатор переменной длины
- Уникальный идентификатор записи. Это поле используется нечасто, но если вам нужно однозначно идентифицировать тег, вы можете создать для него идентификатор.
- Полезная нагрузка переменной длины
- Фактические полезные данные, которые вы хотите прочитать или записать. Сообщение NDEF может содержать несколько записей NDEF, поэтому не предполагайте, что вся полезная нагрузка находится в первой записи NDEF сообщения NDEF.
Система отправки тегов использует поля TNF и type, чтобы попытаться сопоставить тип MIME или URI с сообщением NDEF. В случае успеха он инкапсулирует эту информацию внутри намерения ACTION_NDEF_DISCOVERED
вместе с фактической полезной нагрузкой. Однако бывают случаи, когда система диспетчеризации тегов не может определить тип данных на основе первой записи NDEF. Это происходит, когда данные NDEF не могут быть сопоставлены с типом MIME или URI или когда тег NFC изначально не содержит данных NDEF. В таких случаях объект Tag
, содержащий информацию о технологиях тега и полезной нагрузке, вместо этого инкапсулируется внутри намерения ACTION_TECH_DISCOVERED
.
В таблице 1 описано, как система диспетчеризации тегов сопоставляет поля TNF и типа с типами MIME или URI. Он также описывает, какие TNF нельзя сопоставить с типом MIME или URI. В этих случаях система отправки тегов возвращается к ACTION_TECH_DISCOVERED
.
Например, если система диспетчеризации тегов обнаруживает запись типа TNF_ABSOLUTE_URI
, она сопоставляет поле типа переменной длины этой записи с URI. Система диспетчеризации тегов инкапсулирует этот URI в поле данных намерения ACTION_NDEF_DISCOVERED
вместе с другой информацией о теге, например полезной нагрузкой. С другой стороны, если он встречает запись типа TNF_UNKNOWN
, он вместо этого создает намерение, которое инкапсулирует технологии тега.
Таблица 1. Поддерживаемые TNF и их сопоставления
Тип Формат имени (TNF) | Картирование |
---|---|
TNF_ABSOLUTE_URI | URI на основе поля типа. | TNF_EMPTY | Возвращается к ACTION_TECH_DISCOVERED . | TNF_EXTERNAL_TYPE | URI на основе URN в поле типа. URN кодируется в поле типа NDEF в сокращенной форме: <domain_name>:<service_name> . Android сопоставляет это с URI в форме: vnd.android.nfc://ext/ <domain_name>:<service_name> . | TNF_MIME_MEDIA | Тип MIME на основе поля типа. | TNF_UNCHANGED | Недопустимо в первой записи, поэтому возвращается к ACTION_TECH_DISCOVERED . | TNF_UNKNOWN | Возвращается к ACTION_TECH_DISCOVERED . | TNF_WELL_KNOWN | Тип MIME или URI в зависимости от определения типа записи (RTD), которое вы установили в поле типа. См. Таблицу 2 для получения дополнительной информации о доступных RTD и их сопоставлениях. |
Таблица 2. Поддерживаемые RTD для TNF_WELL_KNOWN и их сопоставления
Определение типа записи (RTD) | Картирование |
---|---|
RTD_ALTERNATIVE_CARRIER | Возвращается к ACTION_TECH_DISCOVERED . | RTD_HANDOVER_CARRIER | Возвращается к ACTION_TECH_DISCOVERED . | RTD_HANDOVER_REQUEST | Возвращается к ACTION_TECH_DISCOVERED . | RTD_HANDOVER_SELECT | Возвращается к ACTION_TECH_DISCOVERED . | RTD_SMART_POSTER | URI на основе анализа полезной нагрузки. | RTD_TEXT | MIME-тип text/plain . | RTD_URI | URI на основе полезной нагрузки. |
Как теги NFC передаются приложениям
Когда система диспетчеризации тегов завершает создание намерения, которое инкапсулирует тег NFC и его идентификационную информацию, она отправляет намерение заинтересованному приложению, которое фильтрует это намерение. Если с намерением могут справиться несколько приложений, отображается окно выбора действия, позволяющее пользователю выбрать действие. Система отправки тегов определяет три намерения, которые перечислены в порядке от высшего к низшему приоритету:
-
ACTION_NDEF_DISCOVERED
: это намерение используется для запуска действия, когда тег, содержащий полезную нагрузку NDEF, сканируется и имеет распознанный тип. Это намерение с наивысшим приоритетом, и система отправки тегов пытается запустить действие с этим намерением раньше, чем с любым другим намерением, когда это возможно. -
ACTION_TECH_DISCOVERED
: если не зарегистрировано никаких действий для обработки намеренияACTION_NDEF_DISCOVERED
, система диспетчеризации тегов пытается запустить приложение с этим намерением. Это намерение также запускается напрямую (без предварительного запускаACTION_NDEF_DISCOVERED
), если сканируемый тег содержит данные NDEF, которые невозможно сопоставить с типом MIME или URI, или если тег не содержит данных NDEF, но относится к известной технологии тегов. -
ACTION_TAG_DISCOVERED
: это намерение запускается, если никакие действия не обрабатывают намеренияACTION_NDEF_DISCOVERED
илиACTION_TECH_DISCOVERED
.
Основной принцип работы системы отправки тегов заключается в следующем:
- Попробуйте запустить Activity с намерением, которое было создано системой диспетчеризации тегов при разборе тега NFC (либо
ACTION_NDEF_DISCOVERED
, либоACTION_TECH_DISCOVERED
). - Если никакие действия не фильтруются для этого намерения, попробуйте запустить действие со следующим намерением с наименьшим приоритетом (
ACTION_TECH_DISCOVERED
илиACTION_TAG_DISCOVERED
), пока приложение не отфильтрует намерение или пока система отправки тегов не перепробует все возможные намерения. - Если ни одно из приложений не фильтрует ни одно из намерений, ничего не делайте.
![](https://developer.android.com/static/images/nfc_tag_dispatch.png?authuser=9&hl=ru)
По возможности работайте с сообщениями NDEF и намерением ACTION_NDEF_DISCOVERED
, поскольку оно является наиболее конкретным из трех. Это намерение позволяет вам запустить приложение в более подходящее время, чем два других намерения, предоставляя пользователю лучший опыт.
Запросить доступ NFC в манифесте Android
Прежде чем вы сможете получить доступ к оборудованию NFC устройства и правильно обработать намерения NFC, объявите эти элементы в файле AndroidManifest.xml
:
- Элемент NFC
<uses-permission>
для доступа к оборудованию NFC:<uses-permission android:name="android.permission.NFC" />
- Минимальная версия SDK, которую может поддерживать ваше приложение. Уровень API 9 поддерживает только ограниченную отправку тегов через
ACTION_TAG_DISCOVERED
и предоставляет доступ к сообщениям NDEF только через дополнительную опциюEXTRA_NDEF_MESSAGES
. Никакие другие свойства тега или операции ввода-вывода недоступны. Уровень API 10 включает в себя комплексную поддержку чтения/записи, а также отправку NDEF на переднем плане, а уровень API 14 обеспечивает более простой способ отправки сообщений NDEF на другие устройства с помощью Android Beam и дополнительные удобные методы для создания записей NDEF.<uses-sdk android:minSdkVersion="10"/>
- Элемент
uses-feature
, чтобы ваше приложение отображалось в Google Play только для устройств, оснащенных оборудованием NFC:<uses-feature android:name="android.hardware.nfc" android:required="true" />
Если ваше приложение использует функциональность NFC, но эта функциональность не имеет решающего значения для вашего приложения, вы можете опустить элемент
uses-feature
и проверить доступность NFC во время выполнения, проверив, имеет лиgetDefaultAdapter()
значениеnull
.
Фильтр по намерениям NFC
Чтобы запустить приложение при сканировании тега NFC, который вы хотите обработать, ваше приложение может фильтровать одно, два или все три намерения NFC в манифесте Android. Однако обычно вы хотите отфильтровать намерение ACTION_NDEF_DISCOVERED
, чтобы максимально контролировать время запуска вашего приложения. Намерение ACTION_TECH_DISCOVERED
является запасным вариантом для ACTION_NDEF_DISCOVERED
когда никакие приложения не фильтруют ACTION_NDEF_DISCOVERED
или когда полезные данные не являются NDEF. Фильтрация для ACTION_TAG_DISCOVERED
обычно является слишком общей категорией, чтобы ее можно было фильтровать. Многие приложения будут фильтровать ACTION_NDEF_DISCOVERED
или ACTION_TECH_DISCOVERED
перед ACTION_TAG_DISCOVERED
, поэтому вероятность запуска вашего приложения мала. ACTION_TAG_DISCOVERED
доступен только в крайнем случае для приложений, подлежащих фильтрации, в тех случаях, когда не установлены другие приложения для обработки намерения ACTION_NDEF_DISCOVERED
или ACTION_TECH_DISCOVERED
.
Поскольку развертывание тегов NFC различается и во многих случаях находится вне вашего контроля, это не всегда возможно, поэтому при необходимости вы можете вернуться к двум другим намерениям. Если у вас есть контроль над типами записываемых тегов и данных, рекомендуется использовать NDEF для форматирования тегов. В следующих разделах описывается, как фильтровать каждый тип намерения.
ACTION_NDEF_DISCOVERED
Чтобы фильтровать намерения ACTION_NDEF_DISCOVERED
, объявите фильтр намерений вместе с типом данных, которые вы хотите фильтровать. В следующем примере фильтруются намерения ACTION_NDEF_DISCOVERED
с помощью MIME-типа text/plain
:
<intent-filter> <action android:name="android.nfc.action.NDEF_DISCOVERED"/> <category android:name="android.intent.category.DEFAULT"/> <data android:mimeType="text/plain" /> </intent-filter>
В следующем примере фильтруется URI в форме https://developer.android.com/index.html
.
<intent-filter> <action android:name="android.nfc.action.NDEF_DISCOVERED"/> <category android:name="android.intent.category.DEFAULT"/> <data android:scheme="https" android:host="developer.android.com" android:pathPrefix="/index.html" /> </intent-filter>
ACTION_TECH_DISCOVERED
Если ваша деятельность фильтрует намерение ACTION_TECH_DISCOVERED
, вы должны создать файл ресурсов XML, в котором указаны технологии, которые поддерживает ваша деятельность, в наборе tech-list
. Ваша деятельность считается совпадением, если набор tech-list
является подмножеством технологий, поддерживаемых тегом, который вы можете получить, вызвав getTechList()
.
Например, если сканируемый тег поддерживает MifareClassic, NdefFormatable и NfcA, в вашем наборе tech-list
должны быть указаны все три, две или одна технология (и ничего больше), чтобы ваша деятельность была сопоставлена.
В следующем примере определяются все технологии. Вы должны удалить те, которые не поддерживаются вашим тегом NFC. Сохраните этот файл (вы можете назвать его как угодно) в папке <project-root>/res/xml
.
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <tech-list> <tech>android.nfc.tech.IsoDep</tech> <tech>android.nfc.tech.NfcA</tech> <tech>android.nfc.tech.NfcB</tech> <tech>android.nfc.tech.NfcF</tech> <tech>android.nfc.tech.NfcV</tech> <tech>android.nfc.tech.Ndef</tech> <tech>android.nfc.tech.NdefFormatable</tech> <tech>android.nfc.tech.MifareClassic</tech> <tech>android.nfc.tech.MifareUltralight</tech> </tech-list> </resources>
Вы также можете указать несколько наборов tech-list
. Каждый из наборов tech-list
рассматривается независимо, и ваша деятельность считается совпадением, если какой-либо отдельный набор tech-list
является подмножеством технологий, возвращаемых getTechList()
. Это обеспечивает семантику AND
и OR
для сопоставления технологий. Следующий пример соответствует тегам, которые могут поддерживать технологии NfcA и Ndef или технологии NfcB и Ndef:
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <tech-list> <tech>android.nfc.tech.NfcA</tech> <tech>android.nfc.tech.Ndef</tech> </tech-list> <tech-list> <tech>android.nfc.tech.NfcB</tech> <tech>android.nfc.tech.Ndef</tech> </tech-list> </resources>
В файле AndroidManifest.xml
укажите только что созданный файл ресурсов в элементе <meta-data>
внутри элемента <activity>
, как показано в следующем примере:
<activity> ... <intent-filter> <action android:name="android.nfc.action.TECH_DISCOVERED"/> </intent-filter> <meta-data android:name="android.nfc.action.TECH_DISCOVERED" android:resource="@xml/nfc_tech_filter" /> ... </activity>
Дополнительные сведения о работе с технологиями тегов и намерении ACTION_TECH_DISCOVERED
см. в разделе «Работа с поддерживаемыми технологиями тегов» в документе Advanced NFC.
ACTION_TAG_DISCOVERED
Чтобы отфильтровать ACTION_TAG_DISCOVERED
используйте следующий фильтр намерений:
<intent-filter> <action android:name="android.nfc.action.TAG_DISCOVERED"/> </intent-filter>
Получить информацию из намерений
Если действие начинается из-за намерения NFC, вы можете получить информацию о отсканированной метке NFC из намерения. В зависимости от сканируемого тега намерения могут содержать следующие дополнительные элементы:
-
EXTRA_TAG
(обязательно): объектTag
представляющий сканируемый тег. -
EXTRA_NDEF_MESSAGES
(необязательно): массив сообщений NDEF, проанализированных из тега. Это дополнение является обязательным для целейACTION_NDEF_DISCOVERED
. -
EXTRA_ID
(необязательно): низкоуровневый идентификатор тега.
Чтобы получить эти дополнительные возможности, проверьте, было ли ваше действие запущено с одним из намерений NFC, чтобы убедиться, что тег был отсканирован, а затем получите дополнительные возможности из намерения. В следующем примере проверяется намерение ACTION_NDEF_DISCOVERED
и получаются сообщения NDEF из дополнительного намерения.
Котлин
override fun onNewIntent(intent: Intent) { super.onNewIntent(intent) ... if (NfcAdapter.ACTION_NDEF_DISCOVERED == intent.action) { intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES)?.also { rawMessages -> val messages: List<NdefMessage> = rawMessages.map { it as NdefMessage } // Process the messages array. ... } } }
Ява
@Override protected void onNewIntent(Intent intent) { super.onNewIntent(intent); ... if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(intent.getAction())) { Parcelable[] rawMessages = intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES); if (rawMessages != null) { NdefMessage[] messages = new NdefMessage[rawMessages.length]; for (int i = 0; i < rawMessages.length; i++) { messages[i] = (NdefMessage) rawMessages[i]; } // Process the messages array. ... } } }
Альтернативно вы можете получить объект Tag
из намерения, который будет содержать полезную нагрузку и позволит вам перечислить технологии тега:
Котлин
val tag: Tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG)
Ява
Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
Создание общих типов записей NDEF.
В этом разделе описывается, как создавать общие типы записей NDEF, которые помогут вам при записи в теги NFC или отправке данных с помощью Android Beam. Начиная с Android 4.0 (уровень API 14), доступен метод createUri()
, который помогает автоматически создавать записи URI. Начиная с Android 4.1 (уровень API 16), доступны createExternal()
и createMime()
, которые помогут вам создавать записи MIME и NDEF внешнего типа. По возможности используйте эти вспомогательные методы, чтобы избежать ошибок при создании записей NDEF вручную.
В этом разделе также описывается, как создать соответствующий фильтр намерений для записи. Все эти примеры записей NDEF должны находиться в первой записи NDEF сообщения NDEF, которое вы записываете в тег или передаете.
TNF_ABSOLUTE_URI
Примечание. Мы рекомендуем использовать тип RTD_URI
вместо TNF_ABSOLUTE_URI
, поскольку он более эффективен.
Вы можете создать запись TNF_ABSOLUTE_URI
NDEF следующим образом:
Котлин
val uriRecord = ByteArray(0).let { emptyByteArray -> NdefRecord( TNF_ABSOLUTE_URI, "https://developer.android.com/index.html".toByteArray(Charset.forName("US-ASCII")), emptyByteArray, emptyByteArray ) }
Ява
NdefRecord uriRecord = new NdefRecord( NdefRecord.TNF_ABSOLUTE_URI , "https://developer.android.com/index.html".getBytes(Charset.forName("US-ASCII")), new byte[0], new byte[0]);
Фильтр намерений для предыдущей записи NDEF будет выглядеть следующим образом:
<intent-filter> <action android:name="android.nfc.action.NDEF_DISCOVERED" /> <category android:name="android.intent.category.DEFAULT" /> <data android:scheme="https" android:host="developer.android.com" android:pathPrefix="/index.html" /> </intent-filter>
TNF_MIME_MEDIA
Запись TNF_MIME_MEDIA
NDEF можно создать следующими способами:
Используя метод createMime()
:
Котлин
val mimeRecord = NdefRecord.createMime( "application/vnd.com.example.android.beam", "Beam me up, Android".toByteArray(Charset.forName("US-ASCII")) )
Ява
NdefRecord mimeRecord = NdefRecord.createMime("application/vnd.com.example.android.beam", "Beam me up, Android".getBytes(Charset.forName("US-ASCII")));
Создание NdefRecord
вручную:
Котлин
val mimeRecord = Charset.forName("US-ASCII").let { usAscii -> NdefRecord( NdefRecord.TNF_MIME_MEDIA, "application/vnd.com.example.android.beam".toByteArray(usAscii), ByteArray(0), "Beam me up, Android!".toByteArray(usAscii) ) }
Ява
NdefRecord mimeRecord = new NdefRecord( NdefRecord.TNF_MIME_MEDIA , "application/vnd.com.example.android.beam".getBytes(Charset.forName("US-ASCII")), new byte[0], "Beam me up, Android!".getBytes(Charset.forName("US-ASCII")));
Фильтр намерений для предыдущей записи NDEF будет выглядеть следующим образом:
<intent-filter> <action android:name="android.nfc.action.NDEF_DISCOVERED" /> <category android:name="android.intent.category.DEFAULT" /> <data android:mimeType="application/vnd.com.example.android.beam" /> </intent-filter>
TNF_WELL_KNOWN с RTD_TEXT
Вы можете создать запись TNF_WELL_KNOWN
NDEF следующим образом:
Котлин
fun createTextRecord(payload: String, locale: Locale, encodeInUtf8: Boolean): NdefRecord { val langBytes = locale.language.toByteArray(Charset.forName("US-ASCII")) val utfEncoding = if (encodeInUtf8) Charset.forName("UTF-8") else Charset.forName("UTF-16") val textBytes = payload.toByteArray(utfEncoding) val utfBit: Int = if (encodeInUtf8) 0 else 1 shl 7 val status = (utfBit + langBytes.size).toChar() val data = ByteArray(1 + langBytes.size + textBytes.size) data[0] = status.toByte() System.arraycopy(langBytes, 0, data, 1, langBytes.size) System.arraycopy(textBytes, 0, data, 1 + langBytes.size, textBytes.size) return NdefRecord(NdefRecord.TNF_WELL_KNOWN, NdefRecord.RTD_TEXT, ByteArray(0), data) }
Ява
public NdefRecord createTextRecord(String payload, Locale locale, boolean encodeInUtf8) { byte[] langBytes = locale.getLanguage().getBytes(Charset.forName("US-ASCII")); Charset utfEncoding = encodeInUtf8 ? Charset.forName("UTF-8") : Charset.forName("UTF-16"); byte[] textBytes = payload.getBytes(utfEncoding); int utfBit = encodeInUtf8 ? 0 : (1 << 7); char status = (char) (utfBit + langBytes.length); byte[] data = new byte[1 + langBytes.length + textBytes.length]; data[0] = (byte) status; System.arraycopy(langBytes, 0, data, 1, langBytes.length); System.arraycopy(textBytes, 0, data, 1 + langBytes.length, textBytes.length); NdefRecord record = new NdefRecord(NdefRecord.TNF_WELL_KNOWN, NdefRecord.RTD_TEXT, new byte[0], data); return record; }
Фильтр намерений для предыдущей записи NDEF будет выглядеть следующим образом:
<intent-filter> <action android:name="android.nfc.action.NDEF_DISCOVERED" /> <category android:name="android.intent.category.DEFAULT" /> <data android:mimeType="text/plain" /> </intent-filter>
TNF_WELL_KNOWN с RTD_URI
Запись TNF_WELL_KNOWN
NDEF можно создать следующими способами:
Используя метод createUri(String)
:
Котлин
val rtdUriRecord1 = NdefRecord.createUri("https://example.com")
Ява
NdefRecord rtdUriRecord1 = NdefRecord.createUri("https://example.com");
Используя метод createUri(Uri)
:
Котлин
val rtdUriRecord2 = Uri.parse("https://example.com").let { uri -> NdefRecord.createUri(uri) }
Ява
Uri uri = Uri.parse("https://example.com"); NdefRecord rtdUriRecord2 = NdefRecord.createUri(uri);
Создание NdefRecord
вручную:
Котлин
val uriField = "example.com".toByteArray(Charset.forName("US-ASCII")) val payload = ByteArray(uriField.size + 1) //add 1 for the URI Prefix payload [0] = 0x01 //prefixes https://www. to the URI System.arraycopy(uriField, 0, payload, 1, uriField.size) //appends URI to payload val rtdUriRecord = NdefRecord(NdefRecord.TNF_WELL_KNOWN, NdefRecord.RTD_URI, ByteArray(0), payload)
Ява
byte[] uriField = "example.com".getBytes(Charset.forName("US-ASCII")); byte[] payload = new byte[uriField.length + 1]; //add 1 for the URI Prefix payload[0] = 0x01; //prefixes https://www. to the URI System.arraycopy(uriField, 0, payload, 1, uriField.length); //appends URI to payload NdefRecord rtdUriRecord = new NdefRecord( NdefRecord.TNF_WELL_KNOWN, NdefRecord.RTD_URI, new byte[0], payload);
Фильтр намерений для предыдущей записи NDEF будет выглядеть следующим образом:
<intent-filter> <action android:name="android.nfc.action.NDEF_DISCOVERED" /> <category android:name="android.intent.category.DEFAULT" /> <data android:scheme="https" android:host="example.com" android:pathPrefix="" /> </intent-filter>
TNF_EXTERNAL_TYPE
Запись TNF_EXTERNAL_TYPE
NDEF можно создать следующими способами:
Используя метод createExternal()
:
Котлин
var payload: ByteArray //assign to your data val domain = "com.example" //usually your app's package name val type = "externalType" val extRecord = NdefRecord.createExternal(domain, type, payload)
Ява
byte[] payload; //assign to your data String domain = "com.example"; //usually your app's package name String type = "externalType"; NdefRecord extRecord = NdefRecord.createExternal(domain, type, payload);
Создание NdefRecord
вручную:
Котлин
var payload: ByteArray ... val extRecord = NdefRecord( NdefRecord.TNF_EXTERNAL_TYPE, "com.example:externalType".toByteArray(Charset.forName("US-ASCII")), ByteArray(0), payload )
Ява
byte[] payload; ... NdefRecord extRecord = new NdefRecord( NdefRecord.TNF_EXTERNAL_TYPE, "com.example:externalType".getBytes(Charset.forName("US-ASCII")), new byte[0], payload);
Фильтр намерений для предыдущей записи NDEF будет выглядеть следующим образом:
<intent-filter> <action android:name="android.nfc.action.NDEF_DISCOVERED" /> <category android:name="android.intent.category.DEFAULT" /> <data android:scheme="vnd.android.nfc" android:host="ext" android:pathPrefix="/com.example:externalType"/> </intent-filter>
Используйте TNF_EXTERNAL_TYPE
для более общего развертывания тегов NFC, чтобы лучше поддерживать устройства как под управлением Android, так и без него.
Примечание . URN для TNF_EXTERNAL_TYPE
имеют канонический формат: urn:nfc:ext:example.com:externalType
, однако спецификация NFC Forum RTD заявляет, что urn:nfc:ext:
URN должна быть исключена из записи NDEF. Поэтому все, что вам нужно указать, — это домен ( example.com
в примере) и тип ( externalType
в примере), разделенные двоеточием. При отправке TNF_EXTERNAL_TYPE
Android преобразует URN urn:nfc:ext:example.com:externalType
в URI vnd.android.nfc://ext/example.com:externalType
, который и объявляет фильтр намерений в примере.
Записи приложений Android
Запись приложения Android (AAR), представленная в Android 4.0 (уровень API 14), обеспечивает большую уверенность в том, что ваше приложение запускается при сканировании метки NFC. AAR содержит имя пакета приложения, встроенное в запись NDEF. Вы можете добавить AAR к любой записи NDEF вашего сообщения NDEF, поскольку Android ищет AAR во всем сообщении NDEF. Если он находит AAR, он запускает приложение на основе имени пакета внутри AAR. Если приложение отсутствует на устройстве, запускается Google Play для его загрузки.
AAR полезны, если вы хотите запретить другим приложениям фильтровать те же цели и потенциально обрабатывать определенные развернутые вами теги. AAR поддерживаются только на уровне приложения из-за ограничения имени пакета, а не на уровне активности, как при фильтрации намерений. Если вы хотите обработать намерение на уровне активности, используйте фильтры намерений .
Если тег содержит AAR, система отправки тегов осуществляет отправку следующим образом:
- Попробуйте запустить действие, используя фильтр намерений, как обычно. Если действие, соответствующее намерению, также соответствует AAR, запустите действие.
- Если действие, которое фильтрует намерение, не соответствует AAR, если несколько действий могут обработать намерение или если ни одно действие не обрабатывает намерение, запустите приложение, указанное в AAR.
- Если ни одно приложение не может запуститься с AAR, перейдите в Google Play, чтобы загрузить приложение на основе AAR.
Примечание. Вы можете переопределить AAR и систему отправки намерений с помощью системы отправки на переднем плане , которая позволяет активности на переднем плане иметь приоритет при обнаружении тега NFC. При использовании этого метода действие должно находиться на переднем плане, чтобы переопределить AAR и систему диспетчеризации намерений.
Если вы по-прежнему хотите фильтровать сканированные теги, не содержащие AAR, вы можете объявить фильтры намерений обычными. Это полезно, если ваше приложение заинтересовано в других тегах, не содержащих AAR. Например, возможно, вы хотите гарантировать, что ваше приложение обрабатывает собственные теги, которые вы развертываете, а также общие теги, развернутые третьими сторонами. Имейте в виду, что AAR специфичны для устройств Android 4.0 или более поздних версий, поэтому при развертывании тегов вы, скорее всего, захотите использовать комбинацию AAR и типов/URI MIME для поддержки самого широкого спектра устройств. Кроме того, при развертывании тегов NFC подумайте о том, как вы хотите записать теги NFC, чтобы обеспечить поддержку большинства устройств (под управлением Android и других устройств). Это можно сделать, определив относительно уникальный тип MIME или URI, чтобы приложениям было легче их различать.
Android предоставляет простой API для создания AAR — createApplicationRecord()
. Все, что вам нужно сделать, это встроить AAR в любое место вашего NdefMessage
. Вы не хотите использовать первую запись вашего NdefMessage
, если только AAR не является единственной записью в NdefMessage
. Это связано с тем, что система Android проверяет первую запись NdefMessage
чтобы определить тип MIME или URI тега, который используется для создания намерения приложений фильтровать. Следующий код показывает, как создать AAR:
Котлин
val msg = NdefMessage( arrayOf( ..., NdefRecord.createApplicationRecord("com.example.android.beam") ) )
Ява
NdefMessage msg = new NdefMessage( new NdefRecord[] { ..., NdefRecord.createApplicationRecord("com.example.android.beam")} ); )
Передача сообщений NDEF на другие устройства
Android Beam обеспечивает простой одноранговый обмен данными между двумя устройствами под управлением Android. Приложение, которое хочет передать данные на другое устройство, должно быть на переднем плане, а устройство, получающее данные, не должно быть заблокировано. Когда передающее устройство вступает в достаточно тесный контакт с принимающим устройством, на передающем устройстве отображается пользовательский интерфейс «Touch to Beam». Затем пользователь может выбрать, передавать ли сообщение на принимающее устройство или нет.
Примечание. Отправка NDEF на передний план была доступна на уровне API 10, который обеспечивает функциональность, аналогичную Android Beam. Эти API с тех пор устарели, но доступны для поддержки старых устройств. Дополнительную информацию см. enableForegroundNdefPush()
.
Вы можете включить Android Beam для своего приложения, вызвав один из двух методов:
-
setNdefPushMessage()
: принимаетNdefMessage
для установки в качестве сообщения для передачи. Автоматически передает сообщение, когда два устройства находятся достаточно близко. -
setNdefPushMessageCallback()
: принимает обратный вызов, содержащийcreateNdefMessage()
, которая вызывается, когда устройство находится в зоне действия для передачи данных. Обратный вызов позволяет создавать сообщение NDEF только при необходимости.
Действие может одновременно отправлять только одно сообщение NDEF, поэтому setNdefPushMessageCallback()
имеет приоритет над setNdefPushMessage()
если установлены оба. Для использования Android Beam необходимо соблюдать следующие общие правила:
- Действие, передающее данные, должно быть на переднем плане. Экраны обоих устройств должны быть разблокированы.
- Вы должны инкапсулировать данные, которые вы передаете, в объект
NdefMessage
. - Устройство NFC, получающее передаваемые данные, должно поддерживать протокол
com.android.npp
NDEF push или SNEP (простой протокол обмена NDEF) NFC Forum. Протоколcom.android.npp
необходим для устройств с уровнем API 9 (Android 2.3) до уровня API 13 (Android 3.2).com.android.npp
и SNEP необходимы на уровне API 14 (Android 4.0) и более поздних версиях.
Примечание. Если ваше действие включает Android Beam и находится на переднем плане, стандартная система отправки намерений отключена. Однако если ваша деятельность также включает приоритетную диспетчеризацию , она все равно может сканировать теги, соответствующие фильтрам намерений, установленным в приоритетной диспетчеризации.
Чтобы включить Android Beam:
- Создайте
NdefMessage
, содержащийNdefRecord
, который вы хотите отправить на другое устройство. - Вызовите
setNdefPushMessage()
с помощьюNdefMessage
или вызовитеsetNdefPushMessageCallback
, передав объектNfcAdapter.CreateNdefMessageCallback
в методеonCreate()
вашей активности. Для этих методов требуется как минимум одно действие, которое вы хотите включить с помощью Android Beam, а также необязательный список других действий для активации.В общем, вы обычно используете
setNdefPushMessage()
если вашему действию нужно только постоянно отправлять одно и то же сообщение NDEF, когда два устройства находятся в пределах досягаемости для связи. Вы используетеsetNdefPushMessageCallback
когда ваше приложение заботится о текущем контексте приложения и хочет отправить сообщение NDEF в зависимости от того, что пользователь делает в вашем приложении.
В следующем примере показано, как простое действие вызывает NfcAdapter.CreateNdefMessageCallback
в методе onCreate()
действия (полный пример см. в разделе AndroidBeamDemo ). В этом примере также есть методы, которые помогут вам создать запись MIME:
Котлин
package com.example.android.beam import android.app.Activity import android.content.Intent import android.nfc.NdefMessage import android.nfc.NdefRecord import android.nfc.NfcAdapter import android.nfc.NfcAdapter.CreateNdefMessageCallback import android.nfc.NfcEvent import android.os.Bundle import android.os.Parcelable import android.widget.TextView import android.widget.Toast import java.nio.charset.Charset class Beam : Activity(), NfcAdapter.CreateNdefMessageCallback { private var nfcAdapter: NfcAdapter? = null private lateinit var textView: TextView override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.main) textView = findViewById(R.id.textView) // Check for available NFC Adapter nfcAdapter = NfcAdapter.getDefaultAdapter(this) if (nfcAdapter == null) { Toast.makeText(this, "NFC is not available", Toast.LENGTH_LONG).show() finish() return } // Register callback nfcAdapter?.setNdefPushMessageCallback(this, this) } override fun createNdefMessage(event: NfcEvent): NdefMessage { val text = "Beam me up, Android!\n\n" + "Beam Time: " + System.currentTimeMillis() return NdefMessage( arrayOf( createMime("application/vnd.com.example.android.beam", text.toByteArray()) ) /** * The Android Application Record (AAR) is commented out. When a device * receives a push with an AAR in it, the application specified in the AAR * is guaranteed to run. The AAR overrides the tag dispatch system. * You can add it back in to guarantee that this * activity starts when receiving a beamed message. For now, this code * uses the tag dispatch system. *///,NdefRecord.createApplicationRecord("com.example.android.beam") ) } override fun onResume() { super.onResume() // Check to see that the Activity started due to an Android Beam if (NfcAdapter.ACTION_NDEF_DISCOVERED == intent.action) { processIntent(intent) } } override fun onNewIntent(intent: Intent) { // onResume gets called after this to handle the intent setIntent(intent) } /** * Parses the NDEF Message from the intent and prints to the TextView */ private fun processIntent(intent: Intent) { textView = findViewById(R.id.textView) // only one message sent during the beam intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES)?.also { rawMsgs -> (rawMsgs[0] as NdefMessage).apply { // record 0 contains the MIME type, record 1 is the AAR, if present textView.text = String(records[0].payload) } } } }
Ява
package com.example.android.beam; import android.app.Activity; import android.content.Intent; import android.nfc.NdefMessage; import android.nfc.NdefRecord; import android.nfc.NfcAdapter; import android.nfc.NfcAdapter.CreateNdefMessageCallback; import android.nfc.NfcEvent; import android.os.Bundle; import android.os.Parcelable; import android.widget.TextView; import android.widget.Toast; import java.nio.charset.Charset; public class Beam extends Activity implements CreateNdefMessageCallback { NfcAdapter nfcAdapter; TextView textView; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); TextView textView = (TextView) findViewById(R.id.textView); // Check for available NFC Adapter nfcAdapter = NfcAdapter.getDefaultAdapter(this); if (nfcAdapter == null) { Toast.makeText(this, "NFC is not available", Toast.LENGTH_LONG).show(); finish(); return; } // Register callback nfcAdapter.setNdefPushMessageCallback(this, this); } @Override public NdefMessage createNdefMessage(NfcEvent event) { String text = ("Beam me up, Android!\n\n" + "Beam Time: " + System.currentTimeMillis()); NdefMessage msg = new NdefMessage( new NdefRecord[] { createMime( "application/vnd.com.example.android.beam", text.getBytes()) /** * The Android Application Record (AAR) is commented out. When a device * receives a push with an AAR in it, the application specified in the AAR * is guaranteed to run. The AAR overrides the tag dispatch system. * You can add it back in to guarantee that this * activity starts when receiving a beamed message. For now, this code * uses the tag dispatch system. */ //,NdefRecord.createApplicationRecord("com.example.android.beam") }); return msg; } @Override public void onResume() { super.onResume(); // Check to see that the Activity started due to an Android Beam if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(getIntent().getAction())) { processIntent(getIntent()); } } @Override public void onNewIntent(Intent intent) { // onResume gets called after this to handle the intent setIntent(intent); } /** * Parses the NDEF Message from the intent and prints to the TextView */ void processIntent(Intent intent) { textView = (TextView) findViewById(R.id.textView); Parcelable[] rawMsgs = intent.getParcelableArrayExtra( NfcAdapter.EXTRA_NDEF_MESSAGES); // only one message sent during the beam NdefMessage msg = (NdefMessage) rawMsgs[0]; // record 0 contains the MIME type, record 1 is the AAR, if present textView.setText(new String(msg.getRecords()[0].getPayload())); } }
Обратите внимание, что этот код закомментирует AAR, который вы можете удалить. Если вы включите AAR, приложение, указанное в AAR, всегда будет получать сообщение Android Beam. Если приложение отсутствует, запускается Google Play для его загрузки. Таким образом, следующий фильтр намерений не является технически необходимым для устройств Android 4.0 или более поздней версии, если используется AAR:
<intent-filter> <action android:name="android.nfc.action.NDEF_DISCOVERED"/> <category android:name="android.intent.category.DEFAULT"/> <data android:mimeType="application/vnd.com.example.android.beam"/> </intent-filter>
Благодаря этому фильтру намерений приложение com.example.android.beam
теперь можно запускать, когда оно сканирует тег NFC или получает Android Beam с AAR типа com.example.android.beam
, или когда сообщение в формате NDEF содержит MIME-запись типа application/vnd.com.example.android.beam
.
Несмотря на то, что AAR гарантируют, что приложение запускается или загружается, рекомендуется использовать фильтры намерений, поскольку они позволяют вам запускать действие по вашему выбору в вашем приложении вместо того, чтобы всегда запускать основное действие в пакете, указанном AAR. AAR не имеют детализации уровня активности. Кроме того, поскольку некоторые устройства под управлением Android не поддерживают AAR, вам также следует на всякий случай встроить идентифицирующую информацию в первую запись NDEF ваших сообщений NDEF и отфильтровать ее. Дополнительную информацию о том, как создавать записи, см. в разделе «Создание общих типов записей NDEF» .