【翻译】NFC近场通信
近场通信(NFC)是一组短距离的无线技术,典型地,需要距离为4cm或更短来初始化一个连接。NFC允许你在一个NFC标签和一个Android设备之间,或在两个Android设备之间共享有效载荷很小的数据。
标签的范围可能非常复杂。简单的标签只提供读和写的语义,有时会通过单次可编程区域来使卡变得只读,更复杂的标签提供了数学操作,并具有加密硬件来验证对于一个扇区的访问。最精致的标签包含操作环境,通过标签上执行的代码而允许复杂的交互。标签中存储的数据也可以以各种各样的格式写入,但Android framework APIs中的大多数是基于并围绕着一种称为NDEF(NFC Data Exchange Format)的NFC Forum标准的。
具有NFC的Android设备同时支持三种主要的操作模式:
- 读/写模式,允许NFC设备读取和/或写入被动式的NFC标签。
- P2P模式,允许NFC设备与其他的NFC设备交换数据;这种操作模式被用于Android Beam。
- 卡模拟模式,允许NFC设备本身就像一个NFC卡一样运行。然后被模拟的NFC卡可以由一个外部的NFC读取器来访问,比如一个NFC销售点的终端。
NFC基础
这篇文档描述了Android是如何处理发现的NFC标签的,以及它是如何将与特定应用相关的数据同志给该应用的。这份文档也会说明如何在你的应用中使用NDEF数据,及支持Android基本NFC功能集合的framework APIs的一个概览。
高级NFC
这篇文档描述了启用Android支持的各种标签技术的APIs。当你不使用NDEF数据时,或当你在使用的NDEF数据Android不能完全理解时,你不得不用你自己的协议栈以原始字节的形式手动地读取或写入标签。在这些情况中,Android提供了一些支持,来探测某中tag技术,并使用你自己的协议栈来打开与标签的通信。
Host-based Card Emulation
这份文档描述了Android设备如何能够不使用一种安全元素而能像NFC卡一样操作,从而使任何Android应用程序来模拟一张卡,并与NFC读取器直接交流。
NFC基础
这份文档描述了你可以在Android上执行的基本的NFC任务。它解释了如何以NDEF消息的形式发送和接收NFC数据,并描述了支持这些功能的Android framework APIs。关于更高级的主题,其中包含了对于使用非NDEF数据的讨论,请参见高级NFC。
当应用NDEF数据和Android时,有两个主要的用例:
- 从一个NFC标签中读取NDEF数据。
- 通过Android Beam,从一个设备beaming NDEF消息到另一个。
从一个NFC标签中读取NDEF数据由标签dispatch系统来处理,该系统会分析发现的NFC标签,适当地分类数据,并启动一个对分类的数据感兴趣的应用程序。想要处理扫描的NFC标签的应用程序可以声明一个intent filter来请求处理该数据。
Android Beam功能允许一个设备通过物理地轻敲一个在一起的设备来推送一个NDEF消息到另一个设备。相较于向Bluetooth等其他无线技术,这种交互提供了一种更简单的方式来发送数据,因为通过NFC,而不需要手动的设备搜索或配对。当两个设备距离在一定范围时,连接会自动启动。通过一些NFC APIs可以来应用Android Beam,因而,任何应用程序可以在设备间传递信息。比如,Contacts,Browser,和YouTube应用程序使用Android Beam来与其他设备共享联系人,网页和视频。
标签Dispatch系统
当屏幕解锁时,Android设备通常都会不停地寻找NFC 标签,除非在设备的Settings菜单中禁用了NFC。当一个Android设备发现一个NFC标签时,想要的行为为使得最适当的activity处理intent,而不需要询问用户要使用什么应用程序。由于设备扫描标签是在一个非常短的距离进行的,要求用户手动选择一个activity很可能会强制他们将设备移离标签而中断连接。你应该开发你的activity来只处理你的activity关心的NFC标签,以避免Activity Chooser的出现。
为了帮助你实现这个目标,Android提供了一个特殊的标签dispatch系统,它可以分析扫描到的NFC标签,解析它们,并试图定位对扫描到的数据感兴趣的应用程序。它通过如下方法来做到这一点:
解析NFC标签,并计算出标签中标识数据载荷的MIME type或这一个URI。
将MIME type或URI及载荷封装进一个intent。开始的这两个步骤在 NFC标签是如何映射到MIME type和URI的 部分描述。
依据intent来启动一个activity。这些在 NFC标签如何被dispatched给应用程序 中描述。
NFC标签是如何映射到MIME type和URI的
在你开始编写你的NFC应用程序之前,理解不同的NFC标签类型,tag dispatch系统是如何解析NFC标签的,及当标签dispatch系统探测到一个NDEF消息时它所做的特殊工作是很重要的。NFC标签可以以多种技术生产,并且也可以以许多种不同的方式来将数据写入它们。Android具有着最多的支持的是NDEF标准,它是由NFC Forum定义的。
NDEF数据被封装在一个消息(NdefMessage)里面,其中包含了一个或多个记录(NdefRecord)。每个NDEF记录必须是符合你想要创建的记录的类型的规范的。Android也支持其它的不包含NDEF数据的标签类型,你可以通过使用android.nfc.tech包中的类来应用它们。为了学习更多关于这些技术的东西,请参见高级NFC主题。应用这些其它的标签类型包括编写你自己的协议栈来与标签通信,因而我们建议你为了开发的简单及最大化的支持Android设备而在可能的时候使用NDEF。
注意:想要下载完整的NDEF规范的话,可以到NFC Forum Specification Download站点,并参考创建通用的NDEF记录类型的示例来了解如何构建NDEF记录。
现在你已经有了一些关于NFC标签的背景知识了,接下来的几节将描述关于Android如何处理NDEF格式化的标签的更具体的信息。当一个Android设备扫描到一个包含了NDEF格式化的数据的NFC标签时,它会解析消息,并尝试去及算出数据的MIME类型或标识URI。为了做到这一点,系统会读取NdefMessage里的第一条NdefRecord来决定如何解释完整的NDEF消息(一个NDEF消息可以具有多个NDEF记录)。在一个符合规范的NDEF消息中,第一个NdefRecord包含如下的域:
3-bit TNF (Type Name Format,类型名称格式)
表示如何解释变量长度类型域。有效的值在Table 1
变量长度类型域
描述了记录的类型。如果使用TNF_WELL_KNOWN,则使用这个域来描述Record Type Definition (RTD)。有效的RTD值在Table 2中描述。
变量长度ID
记录唯一的一个标识符。这个域通常不使用,但如果你需要唯一的标识一个标签,你可以为它创建一个ID。
变量长度载荷
你想要读取或写入的实际数据载荷。一个NDEF消息可以包含多个NDEF记录,因而,不要假设完整的载荷都在NDEF消息的第一个NDEF记录中。
tag dispatch系统使用TNF和类型域来试着将一个MIME type或者URI映射到NDEF消息。如果成功,它会将那些信息封装在一个包含有实际的载荷的ACTION_NDEF_DISCOVEREDintent中。然而,有些情况下,基于第一个NDEF记录,标签dispatch系统无法决定数据的类型。当NDEF数据无法被映射到一个MIME type或者URI时,或者当NFC标签不包含起始的NDEF数据时,这种情况就会发生。在这些情况中,一个包含有标签的技术信息及载荷的Tag对象则会被封装进一个ACTION_TECH_DISCOVERED intent中。
Table 1描述了标签dispatch系统是如何将TNF和类型域映射MIME type或URI的。它也描述了那些TNFs不能被映射到一个MIME type或URI。在这些情况中,标签dispatch系统使用ACTION_TECH_DISCOVERED。
比如,如果标签dispatch系统遇到了一个类型为TNF_ABSOLUTE_URI的记录,它会将那个记录的变量长度类型域映射到一个URI。标签dispatch系统会将那个URI与关于标签的其他信息,比如载荷,一起封装进一个ACTION_NDEF_DISCOVEREDintent的数据成员中。另一方面,如果它遇到了一个类型为TNF_UNKNOWN的记录,则它创建一个封装了标签的技术信息的intent来替代。
Table 1.支持的TNFs及他们的映射
Type Name Format (TNF) | Mapping |
---|---|
TNF_ABSOLUTE_URI | URI based on the type field. |
TNF_EMPTY | Falls back toACTION_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 type based on the type field. |
TNF_UNCHANGED | Invalid in the first record, so falls back toACTION_TECH_DISCOVERED. |
TNF_UNKNOWN | Falls back toACTION_TECH_DISCOVERED. |
TNF_WELL_KNOWN | MIME type or URI depending on the Record Type Definition (RTD), which you set in the type field. SeeTable 2.for more information on available RTDs and their mappings. |
Table 2.TNF_WELL_KNOWN的支持的RTDs及他们的映射
Record Type Definition (RTD) | Mapping |
---|---|
RTD_ALTERNATIVE_CARRIER | Falls back toACTION_TECH_DISCOVERED. |
RTD_HANDOVER_CARRIER | Falls back toACTION_TECH_DISCOVERED. |
RTD_HANDOVER_REQUEST | Falls back toACTION_TECH_DISCOVERED. |
RTD_HANDOVER_SELECT | Falls back toACTION_TECH_DISCOVERED. |
RTD_SMART_POSTER | URI based on parsing the payload. |
RTD_TEXT | MIME type oftext/plain. |
RTD_URI | URI based on payload. |
NFC标签如何被dispatched到应用程序
当标签dispatch系统完成了创建一个封装了NFC标签及它的标识信息的intent时,它会将那个intent发送给一个过滤了那个intent的对那个inent感兴趣的应用程序。如果有多于一个应用程序可以处理那个intent,则Activity Chooser会出现,以使用户可以选择Activity。标签dispatch系统定义了三种intents,按优先级从高到低排列如下:
- ACTION_NDEF_DISCOVERED:当一个包含了一个NDEF载荷的标签被扫描到时,这个intent用于启动一个Activity,它属于一个可识别类型。这是最高优先级的intent,只要可能,标签dispatch系统会在任何其他的intent之前,试着以这种intent来启动一个Activity。
- ACTION_TECH_DISCOVERED:如果没有activities注册了要去处理ACTION_NDEF_DISCOVEREDintent,则标签dispatch系统会试着以这种intent启动一个应用程序。如果标签被扫描到包含了NDEF数据但无法被映射到一个MIME type或URI,或者标签不包含NDEF数据但属于一个已知的标签技术,则会直接以这种intent启动(不会先启动ACTION_NDEF_DISCOVERED)。
- ACTION_TAG_DISCOVERED:如果没有activities处理ACTION_NDEF_DISCOVERED或者ACTION_TECH_DISCOVEREDintents。
标签系统工作的基本流程如下:
- 试着以标签dispatch系统解析NFC标签时创建的intent来启动一个Activity (ACTION_NDEF_DISCOVERED或ACTION_TECH_DISCOVERED)。
- 如果没有activities过滤那样的intent,则试着以更低优先级的intent (ACTION_TECH_DISCOVERED或ACTION_TAG_DISCOVERED)来启动一个Activity,直到一个应用程序过滤了那个intent或直到标签dispatch系统试遍了所有可能的intents。
- 如果没有应用程序过滤任何一种intent,则什么都不做。
Figure 1.Tag Dispatch System
只要有可能,请使用NDEF消息和ACTION_NDEF_DISCOVEREDintent,因为它是三种中最规范的一种。相对于另外两种intents,这种intent允许你在一个更合适的时间点来启动你的应用程序,并给用户一个更好的体验。
在Android Manifest中请求NFC访问权限
在你访问一个设备的NFC硬件并适当的处理NFC intents之前,在你的AndroidManifest.xml文件中声明如下的项目:
<uses-permission android:name="android.permission.NFC" />
你的应用程序能支持的最小的SDK版本。API level 9只能通过ACTION_TAG_DISCOVERED有限地支持标签dispatch,并只能通过EXTRA_NDEF_MESSAGESextra来访问NDEF消息。没有其他的标签属性或I/O操作可用。APIlevel 10包含了广泛的读者/写者支持及前景NDEF推送,而API level 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元素,而通过检查getDefaultAdapter()返回值是否为null在运行时检查NFC的可用性。
过滤NFC Intents
为了在你想要处理的NFC标签被扫描到时启动你的应用程序,你的应用程序可以在Android manifest中过滤一个,两个或所有的三个NFC intents。然而,为了当你的应用程序启动时能有最大化的控制权,你通常想要过滤ACTION_NDEF_DISCOVERED。ACTION_TECH_DISCOVEREDintent是ACTION_NDEF_DISCOVERED的替补,当没有应用程序过滤ACTION_NDEF_DISCOVERED或当载荷不是NDEF时使用。过滤ACTION_TAG_DISCOVERED通常是一个很泛泛的过滤种类。许多应用程序在过滤ACTION_TAG_DISCOVERED之前,将会先过滤ACTION_NDEF_DISCOVERED或ACTION_TECH_DISCOVERED,因而你的应用程序将会有很低的被启动的可能性。只要当没有安装其他的应用程序来处理ACTION_NDEF_DISCOVERED或ACTION_TECH_DISCOVEREDintent的情况下,才会使用ACTION_TAG_DISCOVERED来作为最后的过滤应用程序的依据。
由于NFC标签部署变化无常,而且大多不在你的控制之下,则这不总是可能的,这也是为什么在需要的时候你可以使用其他两种intents的替补。当你可以控制标签的类型及写入的数据时,则建议你使用NDEF来格式化你的标签。下面的几节描述如果过滤每种类型的intent。
ACTION_NDEF_DISCOVERED
为了过滤ACTION_NDEF_DISCOVEREDintents,则声明包含有你想要过滤的数据的类型的intent filter。下面的例子过滤具有一个值为text/plain的MIME type的ACTION_NDEF_DISCOVEREDintent。:
<intent-filter> <action android:name="android.nfc.action.NDEF_DISCOVERED"/> <category android:name="android.intent.category.DEFAULT"/> <data android:mimeType="text/plain" /></intent-filter>
下面的例子过滤一个形为http://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="http" android:host="developer.android.com" android:pathPrefix="/index.html" /></intent-filter>
ACTION_TECH_DISCOVERED
如果你的activity过滤ACTION_TECH_DISCOVEREDintent,则你必须创建一个XML资源文件在一个tech-list集合中描述你的activity支持的技术。如果一个tech-list集合是标签支持的技术的子集,则你的activity将被认为是一个匹配,你可以通过调用getTechList()来获取标签支持的技术。
比如,如果被扫描到的标签支持MifareClassic,NdefFormatable,和NfcA,则你的tech-list集合必须描述所有的三种、两种或一种技术(并且没有其它),以使得你的activity能够被匹配上。
下面的例子定义了所有的技术。你可以移除你不需要的那些。将这个文件(你可以以任何你喜欢的名字命名)保存在<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></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>
关于使用tag技术及ACTION_TECH_DISCOVEREDintent的更多信息,请参考高级NFC文档中的Working with Supported Tag Technologies。
ACTION_TAG_DISCOVERED
为了过滤ACTION_TAG_DISCOVERED,使用如下的intent filter:
<intent-filter> <action android:name="android.nfc.action.TAG_DISCOVERED"/></intent-filter>
从intents中获取信息
如果一个activity是由于一个NFC intent而被启动的,则你可以从intent中获取关于所扫描到的NFC标签的信息。依赖于所扫描到的标签,intents可能包含如下的extras:
- EXTRA_TAG (必需的):一个Tag对象表示被扫描的标签。
- EXTRA_NDEF_MESSAGES(optional): An array of NDEF messages parsed from the tag. This extra is mandatory onintents.
- EXTRA_NDEF_MESSAGES(可选的):由标签解析来的一个NDEF消息的数组。这个extra在intents上是强制的。
- {@link android.nfc.NfcAdapter#EXTRA_ID(可选的):tag的底层的ID。
为了获取这些extras,可以去检查你的activity是否是由一个NFC intents启动起来的,以确保扫描到了一个标签,然后从intent中获取extras。下面的例子检查ACTION_NDEF_DISCOVEREDintent,并从一个intent extra中获取NDEF消息。
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发送数据时得到帮助。从Android 4.0(API level 14)开始,createUri()方法就可以用来自动地创建URI记录。createExternal()和createMime()可以用来帮助你创建MIME和外部类型的NDEF记录。只要可能,就使用这些辅助方法以避免手动创建NDEF记录时可能出现的错误。
这一节也将描述如何创建对应于那样的记录的intent filter。所有的这些NDEF记录示例应该在你写入一个标签或beaming的NDEF消息的第一个NDEF记录里。
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_MEDIANDEF记录。使用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_KNOWNNDEF记录:
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;}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_KNOWNNDEF记录。使用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 Prefixbyte payload[0] = 0x01; //prefixes http://www. to the URISystem.arraycopy(uriField, 0, payload, 1, uriField.length); //appends URI to payloadNdefRecord 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_TYPENDEF记录。使用createExternal()方法:
byte[] payload; //assign to your dataString domain = "com.example"; //usually your app's package nameString 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);对应于前面的NDEF记录的intent filter看起来则将像下面这样:
<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标 签部署以更好地同时支持Android和非Android设备,则使用 TNF_EXTERNAL_TYPE。
注意:TNF_EXTERNAL_TYPE的URNs具有一个标准的格式:
urn:nfc:ext:example.com:externalType,然而NFCForum RTD规范中声明了urn:nfc:ext:URN的部分必须被从NDEF记录中省略。因而,你需要做的全部就是提供域(例子中的example.com)及类型(例子中的externalType),并用冒号分开。当dispatchingTNF_EXTERNAL_TYPE是,Android将urn:nfc:ext:example.com:externalTypeURN转换为一个vnd.android.nfc://ext/example.com:externalTypeURI,即例子的intent filter中声明的那个。
Android应用程序记录
在Android 4.0(API level 14)中引入,在一个NFC标签被扫面到时你的activity被启动,一个Android Application Record (AAR)提供了一种更强的确定性。一个AAR在一个NDEF记录中嵌入了一个应用程序的包名。因为Android会在整个的NDEF消息中搜索AARs,你可以给你的NDEF消息的任何NDEF记录添加一个AAR。如果它找到一个AAR,它会根据AAR中的包名,启动那个应用程序。如果设备中没有那个应用程序,则Google Play会被启动来下载那个应用程序。
如果你想要阻止其他的应用程序过滤相同的intent,并可能处理你所部署的特定的标签,则AARs是很有用的。AARs只在应用程序层面支持,由于包名的限制,而不再Activity的层面,即intent过滤的层面。如果你想要在Activity的层面处理一个intent,则使用intent filters。
如果一个标签包含一个AAR,则标签dispatch系统将以如下的方式dispatch:
- 试着正常地使用一个intent filter启动一个Activity。如果那个Activity匹配intent同时也匹配AAR,则启动那个Activity。
- 如果过滤了那个intent的Activity与AAR不匹配,如果有多个Activities可以处理那个intent,或者没有Activity处理那个intent,则启动AAR描述的那个应用程序。
- 如果没有应用程序可以以那个AAR启动,则根据AAR进入Google Play来下载那个应用。
注意:你可以通过前景dispatch系统覆写AARs和intent dispatch系统,这将使得当一个NFC标签被发现时,一个前景activity具有一定的优先权。通过这种方法,activity必须在前景来覆写AARs和intent dispatch系统。
如果你仍然想要过滤所扫描的不包含AAR的标签,你可以像通常那样声明intent filters。如果你的应用程序还对其他的不包含一个AAR的标签感兴趣时,这一点很有用。比如,也许你想要保证你的应用程序掌握所有的你部署的标签,及由第三方所部属的普通的标签。请记住,AARs只在Android 4.0设备或更新的版本中在可用,因而当部署标签时,你很可能想要使用AARs和MIME types/URI的结合来支持最广泛范围的设备。此外,当你部署NFC标签是,请考虑你想要如何写入你的NFC标签来启用对于大多数设备的支持(Android 和其他设备)。你可以通过定义一个相对唯一的MIME type或URI来使应用程序更易于区分来做到这一点。
Android提供了一个简单的API来创建一个AAR,createApplicationRecord()。你要做的就只是把这个AAR嵌入到你的NdefMessage的什么地方。你不要使用你的NdefMessage的第一个记录,除非AAR是NdefMessage中的唯一的记录。这是因为Android系统检查一个NdefMessage的第一个记录来确定标签的MIME type或URI,他们会被用来为应用程序创建一个intent以过滤。下面的代码显示了你要如何创建一个AAR:
NdefMessage msg = new NdefMessage( new NdefRecord[] { ..., NdefRecord.createApplicationRecord("com.example.android.beam")}
Beaming NDEF 消息到其他的设备
Android Beam允许在两个Android设备之间进行简单的点到点数据交换。想要beam数据到另外的设备的应用程序必须在前台,而接收数据的设备必须是没有锁屏的。当beaming设备与接收设备靠得足够近时则会进行联系,beaming设备显示“Touch to Beam” UI。然后用户选择是否要beam消息到接收设备。
注意:前台NDEF推送在API level 10是可用的,其提供了与Android Beam类似的功能。这些API已经被废弃了,但仍然是可用的,以支持较老的设备。请参考enableForegroundNdefPush()来获取更多的信息。
你可以通过调用两个方法中的一个来为你的应用程序启用Android Beam:
- setNdefPushMessage():接收一个NdefMessage作为要beam的消息。当两个设备靠得足够近时自动地beam消息。
- setNdefPushMessageCallback():接收一个回调,其中包含有一个createNdefMessage(),当有设备在beam数据的范围时回调被调用。回调能够使你只在需要的时候才创建NDEF消息。
一个activity在某一时刻只能推送一条NDEF消息,因而如果两者都被设置了,则setNdefPushMessageCallback()优先于setNdefPushMessage()。为了使用Android Beam,则必须满足下面的大体的guidelines:
- beaming数据的activity必须在前台。两台设备必须都是屏幕解锁的。
- 你必须把你要beaming的数据封装进一个NdefMessage对象。
- 接收被beamed的数据的NFC设备必须支持com.android.npp NDEF推送协议或NFC Forum的SNEP(Simple NDEF Exchange Protocol)。在API level 9(Android 2.3)到API level 13(Android 3.2)上设备需要com.android.npp协议。在API level 14(Android 4.0)及之后,则com.android.npp和SNEP都需要。
注意:如果你的activity启用了Android Beam,并且在前台,则标准的intent dispatch系统会被禁用。然而如果你的activity也启用了前台dispatching,则它仍然可以扫描匹配了在前台dispatching中设置的intent filters的标签。
启用Android Beam:
- 创建一个NdefMessage,其中包含了你想要推送到另外的设备的NdefRecords。
- 以一个NdefMessage为参数调用setNdefPushMessage(),或在你的activity的onCreate()方法中传递一个NfcAdapter.CreateNdefMessageCallback对象来调用setNdefPushMessageCallback。这些方法至少需要一个你想用通过Android Beam启用的activity,同时其他的想要激活的activity则是可选的。通常,如果你的Activity只需要在所有时候都推送相同的NDEF消息,则正常地使用setNdefPushMessage()即可,当两个设备距离足够近时通信。当你的应用程序关心应用程序当前的上下文时,或者想要推送的一个NDEF消息依赖于你的应用程序中用户正在做的事时,则使用setNdefPushMessageCallback。
下面的例子显示了一个简单的activity如何在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会被启动起来来下载那个应用。因此,对于Andriod 4.0或之后的版本,如果使用了AAR,则在技术上下面的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>
通过这个intent filter,com.example.android.beam应用现在可以在它扫描到一个NFC标签或接收到具有一个类型为com.example.android.beam的AAR的Android Beam时,或者当一个NDEF格式化的消息包含了一个类型application/vnd.com.example.android.beam的MIME记录时,被启动了。
即使AARs保证一个应用程序被启动或下载,仍然建议使用intent filters,因为他们使你启动你的应用中你选择的Activity,而不是总启动由一个AAR描述的包内的主Activity。同时,由于一些Android设备不支持AARs,你也应当在你的NDEF消息的第一个NDEF记录中嵌入标识信息,以防万一,并过滤它们。参考创建NDEF记录的通用类型来获取更多如何创建记录的信息。
Done.
更多相关文章
- 使用Qt开发Android应用程序(Qt on Android),连接安卓手机真机调试时
- MTK平台修改Bootloader源代码,让Android设备一通电就自动开机
- Android学习五之Service
- Android中获取后台正在运行的应用列表(附源码)
- 在 Android(安卓)上使用 XML 和 JSON,第 1 部分: 在 Android(安卓
- Android爪机连接蓝牙设备并进行通信的小例子
- 为Android的apk应用程序文件加壳以防止反编译的教程
- 6 个可以让代码变得更整洁的 Android(安卓)库
- Android:创建可穿戴应用 - 建立模拟器和创建项目