本文档介绍了您在 Android 中执行的基本 NFC 任务。它解释了如何以 NDEF 消息的形式发送和接收 NFC 数据,并介绍了支持这些功能的 Android 框架 API。有关更高级的主题,包括处理非 NDEF 数据的讨论,请参阅高级 NFC。
从 NFC 标签读取 NDEF 数据由标签调度系统处理,该系统会分析发现的 NFC 标签,对数据进行适当分类,并启动对分类数据感兴趣的应用。想要处理扫描到的 NFC 标签的应用可以声明 Intent 过滤器并请求处理数据。
标签调度系统
通常,当屏幕解锁时,Android 设备会寻找 NFC 标签,除非在设备的“设置”菜单中停用了 NFC 功能。当 Android 设备发现 NFC 标签时,理想的行为是让最合适的 Activity 处理 Intent,而无需询问用户要使用哪个应用。由于设备是在极短距离内扫描 NFC 标签的,因此让用户手动选择 Activity 可能会迫使他们将设备从标签移开,从而断开连接。您应该开发您的 Activity,使其仅处理您的 Activity 关心的 NFC 标签,以防止出现 Activity 选择器。
为了帮助实现此目标,Android 提供了一个特殊的标签调度系统,该系统会分析扫描到的 NFC 标签,解析它们,并尝试定位对扫描数据感兴趣的应用。它的工作方式如下:
- 解析 NFC 标签,找出标识标签中数据负载的 MIME 类型或 URI。
- 将 MIME 类型或 URI 以及负载封装到 Intent 中。前两个步骤在NFC 标签如何映射到 MIME 类型和 URI中进行了描述。
- 根据 Intent 启动一个 Activity。这在NFC 标签如何调度到应用中进行了描述。
NFC 标签如何映射到 MIME 类型和 URI
在开始编写 NFC 应用之前,了解不同类型的 NFC 标签、标签调度系统如何解析 NFC 标签以及标签调度系统在检测到 NDEF 消息时所做的特殊工作非常重要。NFC 标签的技术多种多样,并且可以使用多种不同的方式向其中写入数据。Android 对由NFC Forum定义的 NDEF 标准支持最多。
NDEF 数据封装在包含一个或多个记录(NdefRecord
)的消息(NdefMessage
)中。每个 NDEF 记录必须根据您想要创建的记录类型的规范格式良好。Android 还支持不包含 NDEF 数据的其他类型的标签,您可以使用 android.nfc.tech
包中的类来处理这些标签。要了解有关这些技术的更多信息,请参阅高级 NFC 主题。处理这些其他类型的标签涉及编写您自己的协议栈与标签通信,因此我们建议尽可能使用 NDEF,以便于开发并最大程度地支持 Android 设备。
注意:要下载完整的 NDEF 规范,请访问NFC Forum 规范和应用文档网站,并参阅创建常用类型的 NDEF 记录以获取构建 NDEF 记录的示例。
现在您已经对 NFC 标签有了一些背景知识,以下部分将更详细地描述 Android 如何处理 NDEF 格式的标签。当 Android 设备扫描包含 NDEF 格式数据的 NFC 标签时,它会解析消息并尝试找出数据的 MIME 类型或标识 URI。为此,系统会读取 NdefMessage
中的第一个 NdefRecord
,以确定如何解释整个 NDEF 消息(一个 NDEF 消息可以有多个 NDEF 记录)。在格式良好的 NDEF 消息中,第一个 NdefRecord
包含以下字段:
- 3 位 TNF(类型名称格式)
- 指示如何解释变长类型字段。有效值在表 1中描述。
- 变长类型
- 描述记录的类型。如果使用
TNF_WELL_KNOWN
,请使用此字段指定记录类型定义 (RTD)。有效 RTD 值在表 2中描述。 - 变长 ID
- 记录的唯一标识符。此字段不常用,但如果您需要唯一标识标签,则可以为其创建 ID。
- 变长负载
- 您想要读取或写入的实际数据负载。NDEF 消息可以包含多个 NDEF 记录,因此不要假设完整的负载都在 NDEF 消息的第一个 NDEF 记录中。
标签调度系统使用 TNF 和类型字段尝试将 MIME 类型或 URI 映射到 NDEF 消息。如果成功,它会将该信息与实际负载一起封装在 ACTION_NDEF_DISCOVERED
Intent 中。但是,在某些情况下,标签调度系统无法根据第一个 NDEF 记录确定数据类型。这发生在 NDEF 数据无法映射到 MIME 类型或 URI,或 NFC 标签一开始就不包含 NDEF 数据时。在这种情况下,包含标签技术和负载信息的 Tag
对象会被封装到 ACTION_TECH_DISCOVERED
Intent 中。
表 1描述了标签调度系统如何将 TNF 和类型字段映射到 MIME 类型或 URI。它还描述了哪些 TNF 无法映射到 MIME 类型或 URI。在这些情况下,标签调度系统会回退到 ACTION_TECH_DISCOVERED
。
例如,如果标签调度系统遇到类型为 TNF_ABSOLUTE_URI
的记录,它会将该记录的变长类型字段映射到 URI。标签调度系统会将该 URI 与标签的其他信息(例如负载)一起封装在 ACTION_NDEF_DISCOVERED
Intent 的数据字段中。另一方面,如果遇到类型为 TNF_UNKNOWN
的记录,它会创建一个封装标签技术的 Intent。
表 1. 支持的 TNF 及其映射
类型名称格式 (TNF) | 映射 |
---|---|
TNF_ABSOLUTE_URI |
基于类型字段的 URI。 |
TNF_EMPTY |
回退到 ACTION_TECH_DISCOVERED 。 |
TNF_EXTERNAL_TYPE |
基于类型字段中 URN 的 URI。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),您在类型字段中设置。有关可用 RTD 及其映射的更多信息,请参阅表 2。 |
表 2. 支持的 TNF_WELL_KNOWN RTD 及其映射
记录类型定义 (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 标签及其标识信息的 Intent 后,它会将该 Intent 发送给对该 Intent 设置了过滤器的感兴趣的应用。如果多个应用可以处理该 Intent,则会呈现 Activity 选择器,以便用户可以选择 Activity。标签调度系统定义了三个 Intent,按优先级从高到低排序如下:
-
ACTION_NDEF_DISCOVERED
:当扫描到包含 NDEF 负载且类型已知的标签时,此 Intent 用于启动 Activity。这是优先级最高的 Intent,标签调度系统会尽可能优先尝试使用此 Intent 启动 Activity。注意:从 Android 16 开始,扫描存储 URL 链接(即 URI 方案为“https://”或“http://”)的 NFC 标签将触发
ACTION_VIEW
Intent,而不是ACTION_NDEF_DISCOVERED
Intent。 ACTION_TECH_DISCOVERED
:如果没有 Activity 注册处理ACTION_NDEF_DISCOVERED
Intent,标签调度系统会尝试使用此 Intent 启动应用。如果扫描到的标签包含无法映射到 MIME 类型或 URI 的 NDEF 数据,或者标签不包含 NDEF 数据但属于已知的标签技术,此 Intent 也会直接启动(无需先启动ACTION_NDEF_DISCOVERED
)。ACTION_TAG_DISCOVERED
:如果没有 Activity 处理ACTION_NDEF_DISCOVERED
或ACTION_TECH_DISCOVERED
Intent,则会启动此 Intent。
标签调度系统的基本工作方式如下:
- 尝试使用标签调度系统在解析 NFC 标签时创建的 Intent 启动 Activity(可以是
ACTION_NDEF_DISCOVERED
或ACTION_TECH_DISCOVERED
)。 - 如果没有 Activity 过滤该 Intent,则尝试使用下一个优先级较低的 Intent 启动 Activity(可以是
ACTION_TECH_DISCOVERED
或ACTION_TAG_DISCOVERED
),直到有应用过滤该 Intent 或标签调度系统尝试了所有可能的 Intent。 - 如果没有应用过滤任何 Intent,则不执行任何操作。

在可能的情况下,请使用 NDEF 消息和 ACTION_NDEF_DISCOVERED
Intent,因为它是这三个 Intent 中最具体的。此 Intent 允许您在比其他两个 Intent 更合适的时间启动应用,从而为用户提供更好的体验。
在 Android 清单中请求 NFC 访问
在访问设备的 NFC 硬件并正确处理 NFC Intent 之前,请在 AndroidManifest.xml
文件中声明以下项:
- 用于访问 NFC 硬件的 NFC
<uses-permission>
元素<uses-permission android:name="android.permission.NFC" />
- 您的应用支持的最低 SDK 版本。API 级别 9 仅支持通过
ACTION_TAG_DISCOVERED
进行有限的标签调度,并且仅通过EXTRA_NDEF_MESSAGES
extra 提供对 NDEF 消息的访问。无法访问其他标签属性或 I/O 操作。API 级别 10 包含全面的读/写支持以及前台 NDEF 推送,API 级别 14 提供了额外的便捷方法来创建 NDEF 记录。<uses-sdk android:minSdkVersion="10"/>
uses-feature
元素,以便您的应用仅在具有 NFC 硬件的设备上显示在 Google Play 中<uses-feature android:name="android.hardware.nfc" android:required="true" />
如果您的应用使用 NFC 功能,但该功能对您的应用并不至关重要,则可以省略
uses-feature
元素,并在运行时通过检查getDefaultAdapter()
是否为null
来检查 NFC 是否可用。
过滤 NFC Intent
要在扫描到您想要处理的 NFC 标签时启动您的应用,您的应用可以在 Android 清单中过滤一个、两个或所有三个 NFC Intent。但是,为了最大程度地控制应用何时启动,您通常会过滤 ACTION_NDEF_DISCOVERED
Intent。ACTION_TECH_DISCOVERED
Intent 是 ACTION_NDEF_DISCOVERED
的回退方案,当没有应用过滤 ACTION_NDEF_DISCOVERED
或负载不是 NDEF 时使用。过滤 ACTION_TAG_DISCOVERED
通常过于通用,不适合作为过滤类别。许多应用会在过滤 ACTION_TAG_DISCOVERED
之前过滤 ACTION_NDEF_DISCOVERED
或 ACTION_TECH_DISCOVERED
,因此您的应用启动的概率较低。ACTION_TAG_DISCOVERED
仅作为最后一种手段供应用过滤,用于处理未安装其他应用来处理 ACTION_NDEF_DISCOVERED
或 ACTION_TECH_DISCOVERED
Intent 的情况。
由于 NFC 标签的部署情况各不相同,且很多时候不在您的控制范围内,因此这并非总是可能,这就是为什么您可以在必要时回退到其他两个 Intent。当您可以控制写入的标签和数据类型时,建议您使用 NDEF 格式化您的标签。以下部分描述了如何过滤每种类型的 Intent。
ACTION_NDEF_DISCOVERED
要过滤 ACTION_NDEF_DISCOVERED
Intent,请声明 Intent 过滤器以及您想要过滤的数据类型。以下示例过滤 MIME 类型为 text/plain
的 ACTION_NDEF_DISCOVERED
Intent:
<intent-filter> <action android:name="android.nfc.action.NDEF_DISCOVERED"/> <category android:name="android.intent.category.DEFAULT"/> <data android:mimeType="text/plain" /> </intent-filter>
以下示例过滤形式为 https://developer.android.com/index.html
的 URI:
<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
如果您的 Activity 过滤 ACTION_TECH_DISCOVERED
Intent,则必须创建一个 XML 资源文件,在该文件的 tech-list
集合中指定您的 Activity 支持的技术。如果某个 tech-list
集合是标签支持的技术的子集,则您的 Activity 被视为匹配项,您可以通过调用 getTechList()
获取这些技术。
例如,如果扫描到的标签支持 MifareClassic、NdefFormatable 和 NfcA,则您的 tech-list
集合必须指定这三种技术中的全部、两种或一种(并且没有其他技术),以便您的 Activity 匹配。
以下示例定义了所有技术。您必须移除您的 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()
返回的技术的子集,则您的 Activity 被视为匹配项。这为匹配技术提供了 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
文件中,在 <activity>
元素内的 <meta-data>
元素中指定您刚刚创建的资源文件,如下例所示:
<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
Intent 的更多信息,请参阅高级 NFC 文档中的使用受支持的标签技术。
ACTION_TAG_DISCOVERED
要过滤 ACTION_TAG_DISCOVERED
,请使用以下 Intent 过滤器:
<intent-filter> <action android:name="android.nfc.action.TAG_DISCOVERED"/> </intent-filter>
ACTION_VIEW
从 Android 16 开始,扫描存储 URL 链接的 NFC 标签将触发 ACTION_VIEW
Intent。要过滤 ACTION_VIEW
,请参阅此处
。使用 Android 应用链接
打开应用处理该 URL。
从 Intent 中获取信息
如果 Activity 因 NFC Intent 而启动,您可以从该 Intent 中获取有关扫描到的 NFC 标签的信息。Intent 可以包含以下 extras,具体取决于扫描到的标签:
EXTRA_TAG
(必需):表示扫描到的标签的Tag
对象。EXTRA_NDEF_MESSAGES
(可选):从标签解析出的 NDEF 消息数组。此 extra 在ACTION_NDEF_DISCOVERED
Intent 中是必需的。EXTRA_ID
(可选):标签的低级别 ID。
要获取这些 extras,请检查您的 Activity 是否是使用 NFC Intent 启动的,以确保扫描了标签,然后从 Intent 中获取 extras。以下示例检查 ACTION_NDEF_DISCOVERED
Intent 并从 Intent extra 中获取 NDEF 消息。
Kotlin
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. ... } } }
Java
@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. ... } } }
或者,您可以从 Intent 中获取 Tag
对象,该对象将包含负载并允许您枚举标签的技术:
Kotlin
val tag: Tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG)
Java
Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
创建常用类型的 NDEF 记录
本节描述了如何创建常用类型的 NDEF 记录,以帮助您写入 NFC 标签。从 Android 4.0(API 级别 14)开始,createUri()
方法可用于帮助您自动创建 URI 记录。从 Android 4.1(API 级别 16)开始,createExternal()
和 createMime()
可用于帮助您创建 MIME 和外部类型 NDEF 记录。尽可能使用这些辅助方法,以避免手动创建 NDEF 记录时出错。
本节还描述了如何为记录创建相应的 Intent 过滤器。所有这些 NDEF 记录示例都应位于您写入标签的 NDEF 消息的第一个 NDEF 记录中。
TNF_ABSOLUTE_URI
注意:建议您使用 RTD_URI
类型而不是 TNF_ABSOLUTE_URI
,因为它更高效。
您可以通过以下方式创建 TNF_ABSOLUTE_URI
NDEF 记录:
Kotlin
val uriRecord = ByteArray(0).let { emptyByteArray -> NdefRecord( TNF_ABSOLUTE_URI, "https://developer.android.com/index.html".toByteArray(Charset.forName("US-ASCII")), emptyByteArray, emptyByteArray ) }
Java
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 过滤器如下所示:
<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()
方法
Kotlin
val mimeRecord = NdefRecord.createMime( "application/vnd.com.example.android.beam", "Beam me up, Android".toByteArray(Charset.forName("US-ASCII")) )
Java
NdefRecord mimeRecord = NdefRecord.createMime("application/vnd.com.example.android.beam", "Beam me up, Android".getBytes(Charset.forName("US-ASCII")));
手动创建 NdefRecord
Kotlin
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) ) }
Java
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 过滤器如下所示:
<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>
带有 RTD_TEXT 的 TNF_WELL_KNOWN
您可以通过以下方式创建 TNF_WELL_KNOWN
NDEF 记录:
Kotlin
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) }
Java
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 过滤器如下所示:
<intent-filter> <action android:name="android.nfc.action.NDEF_DISCOVERED" /> <category android:name="android.intent.category.DEFAULT" /> <data android:mimeType="text/plain" /> </intent-filter>
带有 RTD_URI 的 TNF_WELL_KNOWN
您可以通过以下方式创建 TNF_WELL_KNOWN
NDEF 记录:
使用 createUri(String)
方法
Kotlin
val rtdUriRecord1 = NdefRecord.createUri("https://example.com")
Java
NdefRecord rtdUriRecord1 = NdefRecord.createUri("https://example.com");
使用 createUri(Uri)
方法
Kotlin
val rtdUriRecord2 = Uri.parse("https://example.com").let { uri -> NdefRecord.createUri(uri) }
Java
Uri uri = Uri.parse("https://example.com"); NdefRecord rtdUriRecord2 = NdefRecord.createUri(uri);
手动创建 NdefRecord
Kotlin
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)
Java
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 过滤器如下所示:
<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()
方法
Kotlin
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)
Java
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
Kotlin
var payload: ByteArray ... val extRecord = NdefRecord( NdefRecord.TNF_EXTERNAL_TYPE, "com.example:externalType".toByteArray(Charset.forName("US-ASCII")), ByteArray(0), payload )
Java
byte[] payload; ... NdefRecord extRecord = new NdefRecord( NdefRecord.TNF_EXTERNAL_TYPE, "com.example:externalType".getBytes(Charset.forName("US-ASCII")), new byte[0], payload);
上一个 NDEF 记录的 Intent 过滤器如下所示:
<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>
对于更通用的 NFC 标签部署,请使用 TNF_EXTERNAL_TYPE
以更好地支持 Android 设备和非 Android 设备。
注意:TNF_EXTERNAL_TYPE
的 URN 的标准格式为:urn:nfc:ext:example.com:externalType
,但 NFC Forum RTD 规范声明必须从 NDEF 记录中省略 URN 的 urn:nfc:ext:
部分。因此,您只需提供域(示例中的 example.com
)和类型(示例中的 externalType
),用冒号分隔。调度 TNF_EXTERNAL_TYPE
时,Android 会将 urn:nfc:ext:example.com:externalType
URN 转换为 vnd.android.nfc://ext/example.com:externalType
URI,这正是示例中 Intent 过滤器声明的内容。
Android 应用记录
Android 4.0(API 级别 14)引入的 Android 应用记录 (AAR) 更确定地保证了在扫描 NFC 标签时您的应用会启动。AAR 在 NDEF 记录中嵌入了应用的软件包名称。您可以将 AAR 添加到您的 NDEF 消息的任何 NDEF 记录中,因为 Android 会在整个 NDEF 消息中搜索 AAR。如果找到 AAR,它会根据 AAR 中的软件包名称启动应用。如果设备上没有该应用,则会启动 Google Play 以下载该应用。
如果您想阻止其他应用过滤相同的 Intent 并可能处理您部署的特定标签,AAR 会很有用。由于软件包名称的限制,AAR 仅在应用级别受支持,而不是像 Intent 过滤那样在 Activity 级别受支持。如果您想在 Activity 级别处理 Intent,请使用 Intent 过滤器。
如果标签包含 AAR,标签调度系统会按以下方式进行调度:
- 尝试像往常一样使用 Intent 过滤器启动 Activity。如果与 Intent 匹配的 Activity 也与 AAR 匹配,则启动该 Activity。
- 如果过滤 Intent 的 Activity 与 AAR 不匹配,或者多个 Activity 可以处理该 Intent,或者没有 Activity 处理该 Intent,则启动由 AAR 指定的应用。
- 如果没有应用可以通过 AAR 启动,则转到 Google Play 以下载基于 AAR 的应用。
注意:您可以使用前台调度系统覆盖 AAR 和 Intent 调度系统,前台调度系统允许前台 Activity 在发现 NFC 标签时具有优先权。使用此方法时,Activity 必须位于前台才能覆盖 AAR 和 Intent 调度系统。
如果您仍然想过滤不包含 AAR 的扫描标签,您可以像往常一样声明 Intent 过滤器。如果您的应用对不包含 AAR 的其他标签感兴趣,这会很有用。例如,您可能想确保您的应用处理您部署的专有标签以及第三方部署的通用标签。请记住,AAR 特定于 Android 4.0 或更高版本的设备,因此在部署标签时,您很可能希望结合使用 AAR 和 MIME 类型/URI 来支持最广泛的设备。此外,在部署 NFC 标签时,请考虑如何写入您的 NFC 标签以支持最多的设备(Android 设备和其他设备)。您可以通过定义一个相对唯一的 MIME 类型或 URI 来实现这一点,以便应用更轻松地区分。
Android 提供了一个简单的 API 来创建 AAR,即 createApplicationRecord()
。您只需将 AAR 嵌入到您的 NdefMessage
的任何位置即可。您不应使用 NdefMessage
的第一个记录,除非 AAR 是 NdefMessage
中唯一的记录。这是因为 Android 系统会检查 NdefMessage
的第一个记录来确定标签的 MIME 类型或 URI,这用于创建供应用过滤的 Intent。以下代码展示了如何创建 AAR:
Kotlin
val msg = NdefMessage( arrayOf( ..., NdefRecord.createApplicationRecord("com.example.android.beam") ) )
Java
NdefMessage msg = new NdefMessage( new NdefRecord[] { ..., NdefRecord.createApplicationRecord("com.example.android.beam")} ); )
用于 NFC 标签扫描的应用允许列表
从 Android 16 开始,当应用首次接收到扫描 NFC 标签的 NFC Intent 时,会通知用户。通知中会提供用户禁止应用继续扫描 NFC 标签的选项。
- 应用可以使用
NfcAdapter.isTagIntentAllowed()
检查用户是否允许该应用扫描 NFC 标签。 - 应用可以通过发送 Intent
ACTION_CHANGE_TAG_INTENT_PREFERENCE
来提示用户再次允许 NFC 标签扫描。
注意:NFC 标签扫描应用允许列表可在 设置 > 应用 > 特殊应用权限 > 通过 NFC 启动
下访问。