Android训练课程(Android Training) - NFC基础

NFC 基础

本文档介绍了在Android上的基本的NFC任务。它说明了如何发送和接收的NDEF消息(NDEF messages)的形式的表单里包含的NFC数据(NFC data),并介绍Android框架里支持这些功能的API。对于更高级的主题,包括与非NDEF数据的讨论,请参阅高级NFC。

当使用NDEF 数据和Android时,有两个主要的使用场景:

•从一个NFC 标签里读取NDEF 数据

•使用 Android Beam™ 快速传输Beaming NDEF messages从一台设备到另一台

<译者注:Android Beam是android系统的一个传输套件,对于具有NFC设备的手机,可通过该功能在两台手机之间传输联系人图片等数据,使用方法也很简单,将两个手机背靠背,这时候位于上面的手机会提示你“敲击屏幕即可穿数据”>

从一个NFC tag里读取NDEF data 将会经过 tag dispatch system,分析被发现的NFC标签,进行适当的数据归类,并启动一个对该分类的数据感兴趣的应用程序。那些想要处理被扫描到的NFC标签的应用程序可以声明一个意图过滤器(declare an intent filter ),并要求处理这些数据。

Android Beam™功能,它允许通过轻轻敲击设备的方式,推送一个NDEF message从一台设备到另一台设备上。这种相互作用提供了一个简单的方法来发送数据,比其它无线技术,比如蓝牙,因为有了NFC,不再需要手动设置发现或配对(译者注:蓝牙需要设置搜索设备和配对)。当两个设备进入范围内(译者注:NFC要求在几厘米内),自动启动连接。 Android Beam可通过一组NFC API被使用,因此,任何应用都可以在设备之间传输信息。例如,联系人,浏览器和YouTube应用程序使用Android Beam与其他设备共享联系人,网页和视频。

标签分发系统 (The Tag Dispatch System)

Android的设备通常是在屏幕解锁的时候寻找NFC标签,除非在设置菜单中禁用了NFC设备。当Android手机发现了一个NFC标签,所希望的行为是最适当的activity来处理它,而不是要求用户选择处理它的应用程序。因为设备扫描NFC标签,在很短的范围内,它很可能导致当用户手动选择(处理该tag的应用)时强行移动设备远离标签而断开连接。您最好让你开发的应用程序仅仅关注您制定的NFC标签,以防止用户手动选择处理的活动的页面出现。

为了帮助你实现这个目标,Android提供了一个特殊的标签分发系统,它会分析被扫描到的NFC标签,解析他们,并试图定位到对这个被扫描到的标签感兴趣的应用程序。这是通过:

1。解析NFC标签和搞清楚MIME类型,或者被包含在标签中的有标记的一个URI。

2。先封装MIME类型或URI,在装入一个intent内。前两个步骤中描述了NFC标签是如何映射到MIME类型和URI的。

3。使用封装好的intent启动应用程序。这是描述如何将NFC标签分派到对其感兴趣的应用程序。

<译者注:android 的标签分发系统做了下面一些事情:解析标签里的数据,并装入intent内,并启动关注该类型的标签的应用程序>

NFC 标签是如何被映射到 MIME 类型和URIs 的

NFC标签是如何映射到MIME类型和URI

在你开始写你的NFC应用之前,重要的是要了解不同类型的NFC标签,标签分发系统如何解析NFC标签,当它(标签分发系统)检测到一个NDEF消息后如何分发到应用程序。NFC标签是一种比较广泛的技术(译者注:标签的种类样式多),也有许多不同的数据写入方式。 Android最大化的支持NDEF标准,它是由NFC论坛(NFC Forum.)定义的。

NDEF数据被封装一个消息(NdefMessage)的内部,一个消息包含一个或多个的记录(NdefRecord)。每个NDEF记录必须是格式有效的,符合规范的。当然,你的NDEF记录也可以符合你创建的类型的规范。 Android还支持其他不包含NDEF数据的标签,您可以通过使用包含在android.nfc.tech包的类来实现它。要了解有关这些技术的更多信息,请参见高级NFC主题。工作涉及到编写自己的协议栈与这些其他类型的标签进行通信,因此我们建议在可能易于开发的情况下使用NDEF和采用Android的设备的最大支持。

注意:要下载完整的NDEF规格,请到NFC Forum Specification Download 页面下载和查看 《创建一般类型的NDEF记录Creating common types of NDEF records 》寻找如何建设NDEF记录的示例。

现在,你有NFC标签的一些背景知识,下面的章节更详细描述了Android是如何处理NDEF格式的标签的。当Android手机扫描一个包含了NDEF格式数据的NFC标签,解析消息,并试图找出数据的MIME类型或标识URI。要做到这一点,系统读取Ndef Message里面的第一个NdefRecord,以确定如何解释整个NDEF消息(NDEF消息可以包含多个NDEF记录)。在一个格式良好的NDEF消息中,第一个NdefRecord包含以下字段:

3-bit TNF (Type Name Format) - 类型名格式

指示如何解释变量长度类型字段。有效的值记载在表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 object),该对象具有有关标签内容的技术信息和有效载荷,将会被封装到一个ACTION_TECH_DISCOVERED intent内。

表1中。介绍了标签分发系统如何映射TNF和类型字段到MIME类型或URIs。同时也说明了哪些TNFs不能被映射到MIME类型或URI。在这种情况下,标签分发系统将回退到ACTION_TECH_DISCOVERED的方式。

例如,如果标签分发系统遇到一个TNF_ABSOLUTE_URI类型的纪录,将该记录的变量长度类型字段映射到一个URI。标签分发系统封装封装那个URI到一个 ACTION_NDEF_DISCOVERED intent的一个数据字段内,以及与其他的标签信息,比如其他实际负载。另一方面,如果它遇到的记录类型是TNF_UNKNOWN,它将创建标签的技术信息的封装。

Table 1. 支持 TNFs and 对应的映射

Type Name Format (TNF)

Mapping

TNF_ABSOLUTE_URI

基于类型字段的URI.

TNF_EMPTY

备选 ACTION_TECH_DISCOVERED.

TNF_EXTERNAL_TYPE

URI based on the URN in the type field. The URN is encoded into the NDEF type field in a shortened form: <domain_name>:<service_name>. Android maps this to a URI in the form: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 依赖于 记录类型定义( Record Type Definition RTD), 这是您设置的类型字段. 参考 Table 2. 获得更多的信息关于 有效的 RTDs 和 对应的映射.

Table 2. 支持 RTDs 关于 TNF_WELL_KNOWN 和对应的 映射

Record Type Definition (RTD)

Mapping

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 filter)的应用程序。如果一个以上的应用程序可以处理的该intent,活动选择器将被启动,使得用户可以选择哪一个应用程序。标签调度系统定义了三种intent,下面从最高优先级到最低优先级的顺序展示它们:

ACTION_NDEF_DISCOVERED:当一个标签被扫描,包含有NDEF有效载荷或者是一个可识别的类型,将会创建一个这样的intent 用来启动一个应用程序。这是最高优先级的意图,标签分发系统会尽可能的使用这种intent而不使用其他方式来启动一个应用。<译者注:尽可能直接对应一个应用而防止出现让用户选择活动>
ACTION_TECH_DISCOVERED:如果没有活动注册处理ACTION_NDEF_DISCOVERED intent,标签分发系统尝试用这个意图启动应用程序。如果标签被扫描包含有NDEF数据但不能被映射到一个MIME类型或URI,或者如果标签不包含NDEF数据但同时是一个已知的标签技术,此intent也可直接被启动(没有ACTION_NDEF_DISCOVERED优先)。
ACTION_TAG_DISCOVERED:如果没有活动处理ACTION_NDEF_DISCOVERED 或者 ACTION_TECH_DISCOVERED的inent,那么这个inent将被启动。.
标签分发系统的工作原理,基本方法如下:

标签分发系统解析NFC的标签,创建inten,尝试启动一个应用程序(无论是ACTION_NDEF_DISCOVERED或ACTION_TECH_DISCOVERED)的。
如果没有应用的过滤器(intent filter)捕获它,尝试使用下一个低优先级的intent启动一个应用程序(eitherACTION_TECH_DISCOVERED或ACTION_TAG_DISCOVERED),直到被应用程序的过滤器捕获,或者直到标签分发度系统尝试了所有可能的intent。
如果没有应用程序的intent过滤器(inent filter)捕获该intent,什么也不做.
<译者注:android可以在系统配置文件指定某个activity的intent filter,标签分发系统会尝试构建intent来直接对应它,如果失败,就再次尝试更低级别的inent来尝试,直到发现没有任何一个来处理它>

图 1. 标签分发系统

只要有可能,请使用NDEF消息和ACTION_NDEF_DISCOVERED的inent来达成你的工作,因为它是三种inent里最特殊的一个。相比其他两种intent,使用这个intent,您可以启动您的应用程序在一个更合适的时间,从而给用户带来更好的体验.

<译者注:因为ACTION_NDEF_DISCOVERED intent比较特殊,才更加的不容易发生用户不得不手动选择处理该标签的程序的情况出现>

在Android清单文件(Manifest)里要求NFC访问

在您访问NFC硬件设备和妥善处理NFC的intent之前,在你的AndroidManifest.xml文件里声明这些项:

•NFC的硬件访问权限<uses-permission>:

<uses-permission android:name="android.permission.NFC"/>

•您的应用程序可以支持的最低SDK版本。 API级别9只支持通过ACTION_TAG_DISCOVERED有限的标签分发,只给访问NDEF的消息使用EXTRA_NDEF_MESSAGES进行额外的扩展。没有其他变量的属性或I/ O操作可以访问。 API 10级包括全面的读/写支持以及前台NDEF推入, API 14级提供了一个简单的使用Android Beam 对NDEF消息到其他设备的方式来推入和额外的方便的方法创建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() 方法,如果为空,就是没有NFC设备。.

NFC Intents的过滤器

一个你关注的NFC标签进行被扫描盗时,您的应用程序启动处理,您的应用程序可以在清单文件中声明过滤一种,两种或全部三种NFC intent。但是,当应用程序启动时,您通常要筛选ACTION_NDEF_DISCOVERED意图为了更多的控制。ACTION_TECH_DISCOVERED意图是一个备用的方式,当ACTION_NDEF_DISCOVERED时没有被注册到过滤器,或者有效载荷数据不是NDEF数据时。过滤ACTION_TAG_DISCOVERED的intnet属于过于笼统的过滤器类别。许多应用程序会在ACTION_TAG_DISCOVERED之前,先过滤ACTION_NDEF_DISCOVERED或ACTION_TECH_DISCOVERED,所以您的应用程序启动的概率很低。ACTION_TAG_DISCOVERED仅可作为最后的手段,仅仅在没有其他过滤了ACTION_NDEF_DISCOVERED或ACTION_TECH_DISCOVERED的意图的应用程序被安装的情况下才有用。

因为NFC标签的多样化和多次性,往往不在你的控制之下,这并不总是可能的,这就是为什么你在必要时需要备选其他两种意图。当你需要控制标签的类型和数据写入,建议您使用NDEF格式的标签。以下各节描述了如何过滤每种类型的意图.

ACTION_NDEF_DISCOVERED

为了过滤ACTION_NDEF_DISCOVERED intents, 声明一个过滤器并且指定数据类型. 下面演示了如何过滤 ACTION_NDEF_DISCOVERED intents,它的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格式的过滤,过滤格式为 http://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="http"
android:host="developer.android.com"
android:pathPrefix="/index.html" />
</intent-filter>

ACTION_TECH_DISCOVERED

如果你的程序想要过滤 ACTION_TECH_DISCOVERED intent, 你必须创建一个xml文件来描述你的程序支持的标签类型的规范,这个规范文件里包含你支持的最小的技术列表tech-list. 这个列表支持的技术,将会和标签支持的技术进行匹配。在程序运行时,你可以获得这些内容,通过调用getTechList()方法。

比如,一个被扫描到的标签支持 MifareClassic, NdefFormatable, and NfcA, 你的程序里的支持列表里需要指明支持其中的三种,两种,其中一种技术(或者可能都没有) 为了使得你的程序被匹配到.

下面的示例定义了支持的所有的技术. 你可以移除那些你不要的. 保存到 (可随意命名) in the <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 and OR 语义. 下面演示了一个标签匹配支持NfcA and Ndef 技术 或者 可被支持 NfcB and 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>
</resources>

<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<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,查阅Working with Supported Tag Technologies 在NFC高级篇.

ACTION_TAG_DISCOVERED

为了过滤 ACTION_TAG_DISCOVERED 使用下面的方式:

<intent-filter>
<action android:name="android.nfc.action.TAG_DISCOVERED"/>
</intent-filter>

从 intents 中读取信息

如果NFC intent启动一个应用, 你可以从这个 intent中获得更多信息. 该Intents可以从标签里读到到下列扩展信息:

EXTRA_TAG (必选): 一个代表里读取到的标签的 Tag 对象.
EXTRA_NDEF_MESSAGES (可选): 从标签的 NDEF messages 中读取到的一个数据集合. 这个信息是强制的。
{@link android.nfc.NfcAdapter#EXTRA_ID (可选): 标签的低级别的ID.
要获得这些扩展信息,请检查如果您的程序是否被NFC intent启动,并确保一个标签被扫描,这时就可以从intent中读取扩展信息了。下面的示例检查了一个ACTION_NDEF_DISCOVERED的intent,并尝试从包含NDEF消息的intent中读取扩展信息。.

public void onResume() {
super.onResume();
...
if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(getIntent().getAction())) {
Parcelable[] rawMsgs = intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);
if (rawMsgs != null) {
msgs = new NdefMessage[rawMsgs.length];
for (int i = 0; i < rawMsgs.length; i++) {
msgs[i] = (NdefMessage) rawMsgs[i];
}
}
}
//process the msgs array
}

另外,你也可以从intent得到一个tag对象,它将包含有效载荷数据,并允许您列举标签的技术:

Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);

创建常见类型的NDEF纪录

本节介绍如何创建一个通用类型的NDEF记录,有助于你写入到NFC标签,或发送数据与Android Beam。开始采用Android4.0(API级别14)中,createUri()方法可以帮助你自动创建URI类型的记录。在Android4.1(API16级),createExternal()和createMime()是可帮助您创建MIME类型和外部扩展类型NDEF记录。使用这些辅助方法使得在手动创建NDEF记录时尽可能避免错误。

本节还介绍了如何创建相应的intent filter的记录。下面这些NDEF记录示例必须在你的NDEF消息里的第一条NDEF记录内,在你写入标签或者Android Beam时.

TNF_ABSOLUTE_URI

注意: 我们推荐你使用 RTD_URI 类型来代替 TNF_ABSOLUTE_URI, 因为它效率更高.

你可以创建一个 TNF_ABSOLUTE_URI NDEF 记录,按照下面的方式:

NdefRecord uriRecord = new NdefRecord(
NdefRecord.TNF_ABSOLUTE_URI ,
"http://developer.android.com/index.html".getBytes(Charset.forName("US-ASCII")),
new byte[0], new byte[0]);

过滤上面这个NDEF记录的intent filter这么写:

<intent-filter>
<action android:name="android.nfc.action.NDEF_DISCOVERED" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="http"
android:host="developer.android.com"
android:pathPrefix="/index.html" />
</intent-filter>

TNF_MIME_MEDIA

你可以创建一个 TNF_MIME_MEDIA NDEF 记录, 按下面的方式.

使用 createMime() 方法:

NdefRecord mimeRecord = NdefRecord.createMime("application/vnd.com.example.android.beam",
"Beam me up, Android".getBytes(Charset.forName("US-ASCII")));

手动方式创建 NdefRecord :

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这么写:

<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 with RTD_TEXT

你可以创建一个 TNF_WELL_KNOWN NDEF 记录,按照下面的方式:

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这么写:

<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 with RTD_URI

你可以创建一个 TNF_WELL_KNOWN NDEF 记录,按照下面的方式:

使用 createUri(String) 方法:

NdefRecord rtdUriRecord1 = NdefRecord.createUri("http://example.com");

使用 createUri(Uri) 方法:

Uri uri = new Uri("http://example.com");
NdefRecord rtdUriRecord2 = NdefRecord.createUri(uri);

手动创建 NdefRecord :

byte[] uriField = "example.com".getBytes(Charset.forName("US-ASCII"));
byte[] payload = new byte[uriField.length + 1]; //add 1 for the URI Prefix
byte payload[0] = 0x01; //prefixes http://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这么写:

<intent-filter>
<action android:name="android.nfc.action.NDEF_DISCOVERED" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="http"
android:host="example.com"
android:pathPrefix="" />
</intent-filter>

TNF_EXTERNAL_TYPE

你可以创建一个 TNF_EXTERNAL_TYPE NDEF 记录,按照下面的方式:

使用 createExternal() 方法:

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 :

byte[] payload;
...
NdefRecord extRecord = new NdefRecord(
NdefRecord.TNF_EXTERNAL_TYPE, "com.example:externalType", new byte[0], payload);

The intent filter for the previous NDEF records would look like this:

<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 类型是一个更普遍的标签,可以更好的支持android和非android设备.

注意: 关于TNF_EXTERNAL_TYPE 的URNs 有典型格式:urn:nfc:ext:example.com:externalType, 然而 NFC Forum RTD 规范声明: urn:nfc:ext: 部分必须从NDEF记录里被省略. 那么,所有你需要提供的内容是一个域 (比如example.com ) 和类型(比如externalType),并用冒号分割它们. 当标签分发系统分发一个TNF_EXTERNAL_TYPE的标签时, Android 转换urn:nfc:ext:example.com:externalType 格式的URN 成vnd.android.nfc://ext/example.com:externalType 格式的URI, 它就是上面的intent过滤器里声明的演示的样子.

Android 应用程序记录 (AAR)

Android在Android4.0(API等级14)推出一个Android应用程序记录(AAR),AAR提供了更强的确定性,在您的应用程序因为一个NFC标签被扫描时而启动时。 AAR具有嵌入在NDEF记录内的应用程序的包名。您可以添加一个AAR到您的任何NDEF记录NDEF消息内,因为Android搜索整个NDEF消息内的所有AAR。如果它发现AAR,根据里面的AAR的包名称来启动应用程序。如果设备上不存在该应用程序,谷歌PLAY(译者注:应用程序市场)将被启动并导向去下载该应用程序。.

如果你要防止其他应用程序过滤功能可能相同的意图和潜在的处理您已经部署的特定标签,AARs是有用的。因为包名AARS只支持在应用程序级别过滤,而不是在通过使用intetnt过滤器的Activity级别。如果你想处理一个Activity的intent,请使用intent filter。

如果一个标签包含一个AAR,标签分发系统将会以下方式工作:.

尝试按常规的方式使用intent filter过滤器来启动一个activity. 如果该应用匹配了过滤器的规则,同时又匹配了AAR的规则,那么启动该activity.
如果匹配了过滤器的规则,但是未匹配AAR规则;如果一个intent引发多个activity的启动(译者注:会弹出提示用户手动选择);或者没有任何一个activity过滤到一个intent;那么将按照AAR程序规范执行.
如果没有应用程序可以被AAR启动,那么将被引导到Google Play 以下载AAR指向的应用程序。
注意: 你可以使用 前端分发系统( foreground dispatch system)来覆盖AARs和标签分发系统的处理方式, 它允许一个前端正在运行的顶层活动的activity 拥有更高的优先权来处理发现到的标签. 使用这种方法时,必须要在前端顶层主动的覆盖AARs和标签分发系统的行为.

如果你仍然想过滤那些不包含AAR的标签,你可以按常规形式声明一个intent filter. 在你的应用程序对其他不包含AAR的标签有兴趣时,这将很有用. 比如, 或许你想保证你的程序 处理 你不熟的私有标签 as well as 第三方部署的常规标签. 保持关注 Android 4.0 及以后的设备规范, 这样当部署时, 你非常可能想去使用一个组合AARs 和 MIME 类型的/URIs 为了支持更宽广范围的设备. 另外,当你部署 NFC 标签, 考虑一下如果写入你的NFC标签舍得可以支持更多的设备(Android设备和其他设备). 你可以这样做,像定义相对唯一的 MIME 类型 或URI 的方式来使得更容易区分.

Android 提供了一个简单的 API去创建一个AAR, createApplicationRecord(). 你所做的就是在任何位置嵌入一个AAR 到你的Ndef消息内. 你不需要使用你的ndef消息的第一条记录, 除非你仅仅只有一条记录. 这是因为 Android 系统 检测 Ndef消息的第一条记录来决定 MIME 类型 或者 标签的URI, 它常常用于为应用程序过滤intent时创建一个intent. 下面的代码演示了如果创建AAR:

NdefMessage msg = new NdefMessage(
new NdefRecord[] {
...,
NdefRecord.createApplicationRecord("com.example.android.beam")}

Beaming NDEF消息到其他设备

Android Beam可以支持两款Android设备之间的简单的点到点数据交换。想要进行Android Beam数据到另一台设备中的那台的设备的应用程序必须是在前台程序(译者注:活动的),而且接收该数据的设备不能被锁定。当准备进行Android Beam的设备有足够接近的接触到准备接收的设备时,Android Beam设备显示“点击屏幕开始Beam”的窗体。然后,用户可以选择是否Beam消息到接收设备内。.

注意: 前端 NDEF 推送 在API level 10后 是可用的, 它提供了和 Android Beam.类似的功能。那些 APIs 目前已经被弃用, 但是在旧的设备仍然是可用的. 阅读 enableForegroundNdefPush() 可以获得更多信息.

你可以在你的程序里启用 Android Beam ,使用下面两个方法中的任何一个:

setNdefPushMessage(): 接受一个 NdefMessage,作为准备进行beam的消息. 当两个设备非常接近时,自动的进行beams 消息 .
setNdefPushMessageCallback(): 接受一个回调方法,该回调包含了一个 createNdefMessage()方法, 该回调在一个设备在可以beam 的时候被调用. 这个回调方法允许你仅仅在需要的时候创建Ndef消息.
一个Activity 一次只能推送一个NDEF消息,所以如果两者都设置了,setNdefPushMessageCallback()的优先级超过setNdefPushMessage()。使用Android Beam,必须满足以下一般原则:
进行beaming数据的 activity 必须在前端. 两个设备的屏幕都不能被锁定.
你必须封装你准备 beaming的数据到一个 NdefMessage 消息对象.
在NFC的装置,接收的无线传输数据必须支持com.android.npp的NDEF的推送协议或NFC论坛的SNEP(简单的NDEF交换协议)。该com.android.npp协议需要API 9级的Android 2.3设备,直到 API 13级Android 3.2的设备。 com.android.npp和SNEP都是必需的API14级(安卓4.0)和更高版本。
注意: 如果你的activity 启用了 Android Beam 并且当前处于前端活动的, 标准的标签分发系统将不可使用. 然而, 如果你的activity 仍然启用的前端分发系统(foreground dispatching), 这是它仍然能匹配标签,在你i的标签分发系统里匹配intent filters设置.

启用 Android Beam 的方法:

创建一个 NdefMessage,这个消息里包含了你准备推送到其他设备的 NdefRecord .
在你的activity的onCreate()方法中,调用 setNdefPushMessage() 传入参数是一个 NdefMessage 或 者调用 setNdefPushMessageCallback 传入一个 NfcAdapter.CreateNdefMessageCallback 对象. 这些方法都至少需要一个你要启用Android Beam的activity,连同其他激活的activity的可选列表。
在一般情况下,当两个设备都在沟通的范围,如果仅仅推送相同的NDEF消息,您通常使用setNdefPushMessage()。当您的应用程序关心当前应用程序的上下文信息(目前情况),或者希望根据用户在做什么来推动一个NDEF消息,您可以使用 setNdefPushMessageCallback。

下面的示例显示了如何在一个简单的activity的onCreate()方法中调用NfcAdapter.CreateNdefMessageCallback(完整的示例见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;

public class Beam extends Activity implements CreateNdefMessageCallback {
NfcAdapter mNfcAdapter;
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
    mNfcAdapter = NfcAdapter.getDefaultAdapter(this);
    if (mNfcAdapter == null) {
        Toast.makeText(this, "NFC is not available", Toast.LENGTH_LONG).show();
        finish();
        return;
    }
    // Register callback
    mNfcAdapter.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会启动下载该应用程序。因此,Android4.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>

有了这个意图过滤器,现在当它扫描一个NFC标签,或接收到一个AAR的类型com.example.android.beam的android beam消息时,或当收到一个包含了 MIME 记录application/vnd.com.example.android.beam.记录的NDEF格式的消息,现在的com.example.android.beam应用程序将可以被启动。。

即使AARS保证了应用程序被启动或下载,仍然建议使用意图过滤器。因为它可以让你启动您选择的应用程序中的Activity,而不是总是启动一个AAR指定的包内的主Activity。AARs没有Activity级别水平的粒度。同时,由于一些Android系统的设备不支持AARS,以防万一,你也应该嵌入的标识信息在你的NDEF消息的第一条记录里,并且使用过滤器过滤它。关于如何创建记录的更多信息,请参阅Creating Common Types of NDEF records 。

本文翻译自谷歌的文档,转载请注明出处。原文地址:

http://developer.android.com/intl/zh-CN/guide/topics/connectivity/nfc/nfc.html

本人借助google 翻译,有道词典进行的翻译,感谢这些方便的工具提供者们。

同时感谢cnblogs提供的博客功能。

本文的预期读者是具有一些android开发基础的开发人员,在了解开发的知识下,应该能读懂文章的内容。

本人能力有限,翻译会有疏漏和错误,还请读者谅解。

翻译这些内容来源于项目的需要,曾经接到需要读取RFID标签的一个需求。后来到处在网上搜说一些文章,直到发现这 android文档提供的详尽内容,决定用一些时间来翻译它,这样自己阅读也方便,同时也方便那些寻找资料的开发人员。

正文如下:

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,921评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,635评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,393评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,836评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,833评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,685评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,043评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,694评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,671评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,670评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,779评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,424评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,027评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,984评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,214评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,108评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,517评论 2 343

推荐阅读更多精彩内容