NFC 基础知识(NFC Basics) 根据官网个人翻译

来源:互联网 发布:阿里云查看挂载磁盘 编辑:程序博客网 时间:2024/05/21 01:55

此文为我一时兴起花费将近两天翻译,肯定问题多多,慢慢完善,敬请谅解。

官网原文链接:https://developer.android.com/guide/topics/connectivity/nfc/nfc.html#creating-records

NFC 基础知识(NFC Basics


NFC : Near Field Communication 近场通讯

这篇文档介绍了你在安卓里运行的NFC的基本任务。它解释了如何在NDEF消息形式下发送和接收NFC数据,并且描述了支持此项功能的Android框架层的API。更多高级的主题,包括处理non-NDEF数据的讨论,请参阅高级NFC。

这里有两个在Android中处理NDEF数据的主要的案例 
  • 从NFC tag里读取数据
  • 通过Android Beam将NDEF消息从一台设备发送到另一台设备

从NFC tag读取到的数据将被tag dispatch system(标签调度/发布系统)处理,分析发现的NFC  tag并为其适当的分类,然后启动一个与分类数据相匹配的应用。想要处理扫描到的NFC 标签的应用会声明一个intent filter(意图过滤器)并要求处理数据。

Android Beam功能允许一个设备通过(by physically tapping the devices together)推送NDEF消息给另一个设备。这种交互提供了一种更为简单的方式,相比其他无线技术例如蓝牙,有了NFC,无需手动去发现和配对设备。这种连接会在两个设备进入相互范围之内的时候自动启动。Android Beam可以通过设置一组NFC API得到,所以两个设备间的任何应用都可以传输数据。例如,联系人、浏览器或者是YouTuBe应用通过Android Beam与其他设备共享联系人,网页或者视频。

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

安卓设备通常在屏幕已解锁的情况下来寻找NFC  tag,除非NFC功能在设备的设置菜单中被关闭。当安卓设备发现了NFC tag,通常的行为是让一个最合适的activity来处理这个intent而不会询问用户启动哪一个应用。因为设备扫描NFC tag是在一个非常小的范围内,让用户手动选择一个activity会迫使他们移动设备并更有可能让设备远离tag 也因此导致连接被断开。你应该让你的activity只处理其所关心的NFC tag,避免出现选择Activity(Activity Chooser)的状况。

为了帮助你达到目标,Androidi提供了一个特殊的tag发布系统来分析扫描的NFC tag,解析他们,并尝试定位/找出与扫描数据最匹配的应用。大致是通过:
  1. 解析NFC tag并找出MIME类型或者标识一个tag有效负载的URI。
  2. 将MIME类型或者URI以及有效负载封装到一个intent。前两个步骤在How NFC tags are mapped to MIME types and URIs里描述了。
  3. 通过intent启动一个activity。这个在 How NFC Tags are Dispatched to Applications有说明。

NFC tag如何映射MIME 类型和URI(How NFC tags are mapped to MIME types and URIs)

在你开始写你的NFC应用之前,理解NFC tag之间的差异很重要,tag diapatch system如何解析tag,以及它发现NDEF消息之后所做的工作。NFC tag在一系列广泛的技术中流行(come in),也能让数据通过不同的方式被写入。Android最大程度的支持NFC论坛定义的NDEF标准。

NDEF数据被封装到一个包含一个或多个记录的消息(message)里。每一个NDEF记录必须符合类型记录的相关规范,如果你想创建创建一个的话。Android也支持其它不包含NDEF数据的tag,而这些你可以通过使用android.nfc.tech包下面的类来实现。想学习更多这方面的技术,可以参看高级NFC主题。使用其他类型的 tag意味着你要书写自己的协议来和这些 tag通讯,所以我们建议使用NDEF类型,在追求尽可能简单的开发情况下,并且它被大多数安卓设备所支持。
  1. 记录:要下载完整的NDEF规范请到NFC Forum Specification Download网址,通过查阅Creating common types of NDEF records得知如何构建NDEF记录。
既然你已经有了一定的背景知识,那么接下来的章节更多的要描述Android怎样处理NDEF格式化标签。当安卓设备扫描到包含NDEF格式化标签的NFC tag,它解析消息并找出数据的MIME类型或者标志性URI。为了做到这一点,系统会读取到NdefMessage里面的第一条NdefRecord来决定怎么解释整个NDEF消息(一个NDEF消息可以包含许多NDEF记录)。一个形式良好的NDEF消息第一条NdefRecord包含如下的几个字段:

3字节的TNF(格式化类型名字)(3-bit TNF (Type Name Format)

        标示怎样解释可变长度类型字段。有效值请见表1。

可变长度类型(Variable length type

        描述记录的类型。如果使用TNF_WELL_KNOWN,用这个字段指定记录类型定义(RTD: Record Type Definition )。
        有效的RTD值请见表2.

可变长度的ID(Variable length ID

       记录唯一的标识。这个字段不常被使用,但那时如果你需要唯一标识一个tag,你可以为它创建一个ID.

可变长度的有效负载(Variable length payload

        你想读或写的真实数据有效负载。一个NDEF消息能包含许多NDEF记录,所以不要占用NDEF消息的第一条NDEF记录的所有有效负载。

标签发布系统( tag dispatch system)使用TNF和类型字段来(试着)映射MIME类型或者URI到相应的MDEF消息。如果成功了,它会将信息连同有效负载一起压缩到一个ACTION_NDEF_DISCOVER的intent中。然而,也会有许多标签发布系统根据第一条NDEF记录不能完全确定数据类型的情况。这种情况发生在MDEF数据不能映射到一个MIME类型或URI,或者NFC tag的没有以包含NDEF数据作为起始。在这种情况下,一个包含tag技术和有效负载相关信息的Tag对象会被封装到一个ACTION_NDEF_DISCOVER的intent中instaead)。

表1.描述了tag dispatch system如何映射TNF和类型字段到MIME类型或者URI。它也描述了那些TNF不能被映射到MIME类型或者URI。在这些情况下,tag dispatch system(falls back to)ACTION_TECH_DISCOVER.。

例如,当tag dispatch system遇到一个TNF_ABSOLUTE_URI类型的记录,它映射这条记录的可变长度类型字段到URI。tag dispatch system压缩ACTION_DNEF_DISCOVER类型的intent的数据字段中的URI连同有关这个tag的其他信息,如有效负载。另一方面,如果遇到TNF_UNKNOWN类型的记录,它将创建一个压缩了tag技术的intent来替代。

表1.支持的TNF和他们的映射。
类型名称(Type Name Format)(TNF)映射(Mapping)TNF_ABSOLUTE_URI基于该类型字段的URI.TNF_EMPTY回落到ACTION_TECH_DISCOVEREDTNF_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_DISCOVEREDTNF_UNKNOWN回落到ACTION_TECH_DISCOVEREDTNF_WELL_KNOWNMIME类型或者URI取决于你在类型字段里设置的记录类型定义(RTD),参阅表2。获取更多可获得的RTD值和对应的映射。

表2.支持的TNF为TNF_WELL_KNOWN时的RTD和对应的映射。
Record Name Definition(RTD)MappingRTD_ALTERNATIVE_CARRIER回落到ACTION_TECH_DISCOVEREDRTD_HANDOVER_CARRIER回落到ACTION_TECH_DISCOVEREDRTD_HANDOVER_REQUEST回落到ACTION_TECH_DISCOVEREDRTD_HANDOVER_SELECT回落到ACTION_TECH_DISCOVEREDRTD_SMART_POSTER基于解析有效负载的URIRTD_TEXTtext/plain的MIME类型RTD_URI基于有效负载的URI

NFC 标签如何被分派到应用程序(How NFC Tags are Dispatched to Applications)

当 tag dispatch system创建完一个封装有NFC tag和它的标识信息的intent之后,它会将该intent发送给通过此intent过滤出来并与之匹配的应用程序。如果有多个应用可以处理该intent,会弹出选择界面以供用户选择。tag dispatch system 定义了三种intent,按优先级高低分别为:
1. ACTION_NDEF_DISCOVER:这个intent在扫描到一个包含NDEF有效负载的tag时通常会启动一个Activity,并且是公认的一种类型。它的优先级最高,因此tag dispatch system不管在任何情况下都会用这个intent并在其它intent之前 尽全力去启动一个Activity(即:只要有这个intent,任何情况下都优先处理它)。
2. ACTION_TECH_DISCOVER:如果没有activity要去处理ACTION_NDEF_DISCOVER类型的intent(即:没有这种intent),tag dispatch system会通过这个intent(即ACTION_TECH_DISCOVER类型的)去启动一个应用程序。在扫描到包含不能被映射到MIME类型或者URI的NDEF数据或者不包含NDEF数据但是是一种已知的tag技术的tag时,这种intent立即就会被启动(前提是没有ACTION_NDEF_DISCOVER类型的)。
3. ACTION_TAG_DISCOVER:这种intent在既没有ACTION_NDEF_DISCOVER也没有ACTION_TECH_DISCOVER的时候会被启动。

tag dispatch system工作的基本流程如下:
1. 通过在解析NFC tag时由它自己创建的intent去启动一个Activity(ACTION_NDEF_DISCOVER或ACTION_TECH_DISCOVER)。
2. 如果没有与intent匹配的activity,那么就尝试通过下一个最低优先级的intent启动Activity(ACTION_TECH_DISCOVER或ACTION_TAG_DISCOVER),直到找出匹配的应用程序或者它尝试所有可能intent但没有匹配结果。
3. 如果最终都没有找到匹配的应用程序,就什么都不做。

                                                                            图1. Tag Dispatch System

尽可能使用NDEF消息和ACTION_NDEF_DISCOVER类型的intent,因为它是最特殊的一个。这个intent相比其他两种intent允许你在更合适的时间启动你的应用程序,给用户更好的体验。

在Manifest文件中声明NFC权限(Requesting NFC Access in the Android Manifest)

在你得到一个带有NFC功能的设备并正确处理NFC intent之前,先在你的AndroidManifest.xml文件中声明这些项:
  • NFC用户权限 以用来访问NFC硬件:
  1. <uses-permission android:name="android.permission.NFC" />
  • 你的应用程序支持的最小SDK版本。API 9仅仅支持ACTION_TAG_DISCOVERED,而且只允许ERTRA_NDEF_MESSAGE类型的NDEF消息。没有其他标签属性或者I/O操作可以做到。API 10支持读/写以及前台NDEF推送,而到API 14则提供了一种更简单的 即通过Android Beam来推送NDEF消息,还有其他一些便利的创建NDEF记录的方法。
  1. <uses-sdk android:minSdkVersion="10"/>
  • uses-feature元素能让你的应用在谷歌商店中显示在有NFC硬件的设备上:
  1. <uses-feature android:name="android.hardware.nfc" android:required="true" />
如果你的应用是用的NFC功能,但是它对你又不是那么重要,那你就可以忽略uses-feature元素,只是需要在运行时通过判断getDefaultAdapter()是否为空来检测设备是否支持NFC。

NFC intent过滤(Filtering for NFC Intents)

为了在扫描到你希望处理的NFC tag并启动你的应用,你可以在你应用的Android Manifeat里面过滤一种、两种或者是三种全部的NFC intent。然而,你通常需要在你的应用启动时为ACTION_NDEF_DISCOVER intent筛选。ACTION_TECH_DISCOVER类型的intent是ACTION_NDEF_DISCOVER 在没有找到与它(的 intent)相匹配的应用程序或者有效负载不是NDEF的时候的备用选择。而 ACTION_TAG_DISCOVER类型通常用于对过于笼统的类别进行过滤。 许多应用程序会在ACTION_TAG_DISCOVER之前过滤出ACTION_NDEF_DISCOVER 或者ACTION_TECH_DISCOVER,所以你的应用被启动的可能性更低。ACTION_TAG_DISCOVER是在应用程序没有ACTION_NDEF_DISCOVER 和ACTION_TECH_DISCOVER类型intent的时候的最后手段。
因为NFC tag部署多样并且经常不受控制,这并不总是出现,但这是为什么在必要的时候你要依靠其他两种intent。当你掌握了各种类型的标签和数据写入,建议你用NDEF格式化你的标签。接下来的章节描述了如何过滤各种类型的intent。

ACTION_NDEF_DISCOVERED

为了ACTION_NDEF_DISCOVER类型的intent过滤,根据你想过滤的数据类型声明intent-filter。接下来是MIME type为textview/plain的ACTION_NDEF_DISCOVERED 类型的intent的filter示例:
  1. <intent-filter>
  2. <action android:name="android.nfc.action.NDEF_DISCOVERED"/>
  3. <category android:name="android.intent.category.DEFAULT"/>
  4. <data android:mimeType="text/plain" />
  5. </intent-filter>
下面是形如【http://developer.android.com/index.html】的URI的filter示例:
  1. <intent-filter>
  2. <action android:name="android.nfc.action.NDEF_DISCOVERED"/>
  3. <category android:name="android.intent.category.DEFAULT"/>
  4. <data android:scheme="http"
  5. android:host="developer.android.com"
  6. android:pathPrefix="/index.html" />
  7. </intent-filter>

ACTION_TECH_DISCOVERED

如果你activity的filter是为了ACTION_TECH_DISCOVERED类型的intent,那你必须要创建一个通过tech-list集合来指定你的activity支持的技术的XML资源文件。如果你的tech-list是被tag支持技术集合的一个子集,那么你的activity会被考虑匹配,这个技术集合你可以通过getTechList()得到。
例如,如果被扫描的tag支持MifareClasic、MdefFormatable和NfcA,那么为了你的activity被匹配上 你的tech-list必须指定这三个技术而不是两个、一个或者一个都不指定。
下面的例子定义了所有的技术。你可以移除你不需要的。保存这个文件(你可以为它随意命名)到<project-root>/res/xml路径下边。
  1. <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
  2. <tech-list>
  3. <tech>android.nfc.tech.IsoDep</tech>
  4. <tech>android.nfc.tech.NfcA</tech>
  5. <tech>android.nfc.tech.NfcB</tech>
  6. <tech>android.nfc.tech.NfcF</tech>
  7. <tech>android.nfc.tech.NfcV</tech>
  8. <tech>android.nfc.tech.Ndef</tech>
  9. <tech>android.nfc.tech.NdefFormatable</tech>
  10. <tech>android.nfc.tech.MifareClassic</tech>
  11. <tech>android.nfc.tech.MifareUltralight</tech>
  12. </tech-list>
  13. </resources>
你可以指定多个tech-list集合。每一个都会被单独考虑,只要任何一个是getTechList()的子集你的activity都会被考虑匹配。这里还提供了 AND  OR 运算来匹配技术。下面的例子可以匹配支持NfcA和Ndef或者MfcB和Ndef技术的tag。
  1. <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
  2. <tech-list>
  3. <tech>android.nfc.tech.NfcA</tech>
  4. <tech>android.nfc.tech.Ndef</tech>
  5. </tech-list>
  6. </resources>
  7. <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
  8. <tech-list>
  9. <tech>android.nfc.tech.NfcB</tech>
  10. <tech>android.nfc.tech.Ndef</tech>
  11. </tech-list>
  12. </resources>
在你的AndroidManifest.xml文件的<activity>元素下的<meta-data>的元素中指定了资源文件(的路径),如下所示:
  1. <activity>
  2. ...
  3. <intent-filter>
  4. <action android:name="android.nfc.action.TECH_DISCOVERED"/>
  5. </intent-filter>
  6. <meta-data android:name="android.nfc.action.TECH_DISCOVERED"
  7. android:resource="@xml/nfc_tech_filter" />
  8. ...
  9. </activity>
更多关于tag技术和 ACTION_TECH_DISCOVERED的intent请到高级NFC文档中参阅Working with Supported Tag Technologies。

ACTION_TAG_DISCOVERED

为了过滤ACTION_TAG_DISCOVERED类型的需像下面这样定义intent-filter:
  1. <intent-filter>
  2. <action android:name="android.nfc.action.TAG_DISCOVERED"/>
  3. </intent-filter>


得到intent中的信息(Obtaining information from intents)

如果一个activity是因为NFC intent而启动,那么你可以从这个intent中得到扫描的NFC tag的信息。intent根据扫描到的tag可以包含以下extras:
  • EXTRA_TAG(必需的):一个表示扫描到的tag的Tag对象。
  • EXTRA_NDEF_MESSAGE(可选的):一个从tag中解析到的NDEF信息的数组。这个extra强制托管在ACTION_NDEF_DISCOVERED类型的intent中。
  • EXTRA_ID(可选的):标签的低级别 ID。

为了拿到这些extras,需要检查你的activity是否是由NFC intent启动的以确保tag扫描成功,然后才能从intent中拿到这些extras。下面的例子检查了ACTION_NDEF_DISCOVER类型的intent并从它的extra中得到NDEF信息。
  1. public void onResume() {
  2. super.onResume();
  3. ...
  4. if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(getIntent().getAction())) {
  5. Parcelable[] rawMsgs = intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);
  6. if (rawMsgs != null) {
  7. msgs = new NdefMessage[rawMsgs.length];
  8. for (int i = 0; i < rawMsgs.length; i++) {
  9. msgs[i] = (NdefMessage) rawMsgs[i];
  10. }
  11. }
  12. }
  13. //process the msgs array
  14. }
作为选择之一的,你可以从intent中得到一个包含有效负载并允许你列举标签的技术的Tag对象。
  1. Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);

创建常见类型的NDEF记录(Creating Common Types of NDEF Records)

此章节描述了怎样创建常见类型的NDEF记录,在你写NFC tag或者通过Android Beam发送数据的时候帮助你。从Android 4.0(API 14)开始,createUri()方法能帮你自动创建URI记录。从Android 4.1(API 16)开始,createExternal()和createMime()能帮助你创建MIME和外部类型的NDEF记录。使用这些帮助方法让你在手动创建NDEF记录的时候尽可能避免出错。
这个章节也描述了如何为记录创建相应的intent。所有的NDEF记录示例应该在你写或者发送的tag的NDEF消息里的第一条NDEF记录中。

TNF_ABSOLUTE_URI

注意:我们建议你使用RTD_URI来代替TNF_ABSOLUTE_URI,因为它效率更高。
你可以通过下面的方法创建TNF_ABSOLUTE_URI类型的NDEF记录:
  1. NdefRecord uriRecord = new NdefRecord(
  2. NdefRecord.TNF_ABSOLUTE_URI ,
  3. "http://developer.android.com/index.html".getBytes(Charset.forName("US-ASCII")),
  4. new byte[0], new byte[0]);
以前的NDEF记录的intent-filter看起来像下面这个样子:
  1. <intent-filter>
  2. <action android:name="android.nfc.action.NDEF_DISCOVERED" />
  3. <category android:name="android.intent.category.DEFAULT" />
  4. <data android:scheme="http"
  5. android:host="developer.android.com"
  6. android:pathPrefix="/index.html" />
  7. </intent-filter>

TNF_MIME_MEDIA

你可以通过如下示例创建TNF_MIME_MEDIA的NDEF记录:
使用 createMime() 方法:
  1. NdefRecord mimeRecord = NdefRecord.createMime("application/vnd.com.example.android.beam",
  2. "Beam me up, Android".getBytes(Charset.forName("US-ASCII")));
手动创建 NdefRecord
  1. NdefRecord mimeRecord = new NdefRecord(
  2. NdefRecord.TNF_MIME_MEDIA ,
  3. "application/vnd.com.example.android.beam".getBytes(Charset.forName("US-ASCII")),
  4. new byte[0], "Beam me up, Android!".getBytes(Charset.forName("US-ASCII")));
以前的NDEF记录的intent-filter看起来像下面这个样子:
  1. <intent-filter>
  2. <action android:name="android.nfc.action.NDEF_DISCOVERED" />
  3. <category android:name="android.intent.category.DEFAULT" />
  4. <data android:mimeType="application/vnd.com.example.android.beam" />
  5. </intent-filter>

TNF_WELL_KNOWN with RTD_TEXT

你可以通过如下示例创建TNF_WELL_KNOWN的NDEF记录:
  1. public NdefRecord createTextRecord(String payload, Locale locale, boolean encodeInUtf8) {
  2. byte[] langBytes = locale.getLanguage().getBytes(Charset.forName("US-ASCII"));
  3. Charset utfEncoding = encodeInUtf8 ? Charset.forName("UTF-8") : Charset.forName("UTF-16");
  4. byte[] textBytes = payload.getBytes(utfEncoding);
  5. int utfBit = encodeInUtf8 ? 0 : (1 << 7);
  6. char status = (char) (utfBit + langBytes.length);
  7. byte[] data = new byte[1 + langBytes.length + textBytes.length];
  8. data[0] = (byte) status;
  9. System.arraycopy(langBytes, 0, data, 1, langBytes.length);
  10. System.arraycopy(textBytes, 0, data, 1 + langBytes.length, textBytes.length);
  11. NdefRecord record = new NdefRecord(NdefRecord.TNF_WELL_KNOWN,
  12. NdefRecord.RTD_TEXT, new byte[0], data);
  13. return record;
  14. }
intent-filter看起来像下面这个样子:
  1. <intent-filter>
  2. <action android:name="android.nfc.action.NDEF_DISCOVERED" />
  3. <category android:name="android.intent.category.DEFAULT" />
  4. <data android:mimeType="text/plain" />
  5. </intent-filter>

TNF_WELL_KNOWN with RTD_URI

你可以通过如下示例创建TNF_WELL_KNOWN的NDEF记录:
使用 createUri(String) 方法:
  1. NdefRecord rtdUriRecord1 = NdefRecord.createUri("http://example.com");
使用 createUri(Uri) 方法:
  1. Uri uri = new Uri("http://example.com");
  2. NdefRecord rtdUriRecord2 = NdefRecord.createUri(uri);
手动创建 NdefRecord 
  1. byte[] uriField = "example.com".getBytes(Charset.forName("US-ASCII"));
  2. byte[] payload = new byte[uriField.length + 1]; //add 1 for the URI Prefix
  3. byte payload[0] = 0x01; //prefixes http://www. to the URI
  4. System.arraycopy(uriField, 0, payload, 1, uriField.length); //appends URI to payload
  5. NdefRecord rtdUriRecord = new NdefRecord(
  6. NdefRecord.TNF_WELL_KNOWN, NdefRecord.RTD_URI, new byte[0], payload);
intent-filter看起来像下面这个样子:
  1. <intent-filter>
  2. <action android:name="android.nfc.action.NDEF_DISCOVERED" />
  3. <category android:name="android.intent.category.DEFAULT" />
  4. <data android:scheme="http"
  5. android:host="example.com"
  6. android:pathPrefix="" />
  7. </intent-filter>

TNF_EXTERNAL_TYPE

你可以通过如下示例创建TNF_EXTERNAL_TYPE的NDEF记录:
使用 createExternal() 方法:
  1. byte[] payload; //assign to your data
  2. String domain = "com.example"; //usually your app's package name
  3. String type = "externalType";
  4. NdefRecord extRecord = NdefRecord.createExternal(domain, type, payload);
手动创建 NdefRecord 
  1. byte[] payload;
  2. ...
  3. NdefRecord extRecord = new NdefRecord(
  4. NdefRecord.TNF_EXTERNAL_TYPE, "com.example:externalType", new byte[0], payload);
intent-filter看起来像下面这个样子:
  1. <intent-filter>
  2. <action android:name="android.nfc.action.NDEF_DISCOVERED" />
  3. <category android:name="android.intent.category.DEFAULT" />
  4. <data android:scheme="vnd.android.nfc"
  5. android:host="ext"
  6. android:pathPrefix="/com.example:externalType"/>
  7. </intent-filter>
使用更通用的NFC TNF_EXTERNAL_TYPE类型的NFC tag部署更好地支持安卓设备和non-Android-powered设备。
注意:对于TNF_EXTERNAL_TYPE的URN有一个标准的格式:urn:nfc:ext:example.com:externalType,然而NFC论坛的RTD规范声明URN的一部分【urn:nfc:ext:】必须在NDEF记录中被省略。所以你需要提供的是被冒号分隔的域名(example.com in the example)和类型(externalType in the example)。当分发TNF_EXTERNAL_TYPE的时候,Android将【urn:nfc:ext:example.com:externalType】URN转换为【vnd.android.nfc://ext/example.com:externalType】的URL,这些在例子中的intent-filter有声明。

Android应用程序记录(Android Application Records)

在Android 4.0(API 14)中有介绍,Android Application Record (AAR)使得在扫描到NFC tag时启动你的应用程序有了更好的保证。一个带有应用包名的AAR被嵌入一个NFC记录。你能添加一个AAR到任意一个NDEF消息中,因为Android会为AAR搜寻整个NDEF消息。如果找到一个,Google Play会被启动下载该应用程序。

AAR能有效的预防通过同一个intent过滤的其他应用程序 处理被你部署的特定的tags的可能性。  AAR是application支持级别的,因为包名限制,不是带有intent-filter的Activity级别。如果你想在Activity级别处理一个intent,请使用 intent filters.
 
如果一个tag包含AAR,tag diapatch system通过如下方式分发:
1. 像往常一样通过intent-filter启动一个Activity。如果一个Activity匹配这个intent并且同时匹配这个AAR,那么启动它。
2. 如果intent-filter过滤出的Activity不匹配AAR,并且有多个Activity能处理这个intent,或者没有Activity能处理,那么启动AAR指定的应用程序。
3. 如果通过AAR没有应用程序被启动,那么根据AAR到谷歌应用商店里下载一个应用。

注意:你可以重写AAR和前台分发系统的intent发布系统,这允许一个前台Activity在发现NFC tag时拥有优先级。通过这个方法,activity必须在前台重写AAR和intent发布系统。

如果你想为扫描的tag过滤出不包含AAR的,你可以像往常一样声明intent-filter。这对于当你的应用程序对其他不包含AAR的tag感兴趣的时候是有用的。例如,你可能想保证你的应用程序处理你部署的优先级tag或者其他第三方开发的。请记住,AAR对于Android 4.0或者之后的设备是特殊的,所以当你开发tag的时候,你或许会想使用一个AAR和MIME type或者URI的组合以支持大多数的设备。另外,当你部署NFC tag时,想想如何写NFC tag才能让你的tag支持大多数设备(安卓设备或者其他设备)。你可以通过定义一个相关且唯一的MIME类型或者URI来使得应用程序更好的区分。
Android提供一个简单的API来创建AAR, 即 createApplicationRecord()。你需要做的只是在你的 NdefRecord 的任何的地方嵌入AAR。你不会想使用你的 NdefRecord  的第一条记录,除非这个AAR是这个NdefRecord 的唯一记录。这是因为Android系统通过检查NdefRecord 的第一条记录来确定tag的MIME类型或者URI,这被用来为应用创建一个intent来过滤。下面的代码向你展示了如何创一个AAR:
  1. NdefMessage msg = new NdefMessage(
  2. new NdefRecord[] {
  3. ...,
  4. NdefRecord.createApplicationRecord("com.example.android.beam")}

发送NDEF消息到其他设备(Beaming NDEF Messages to Other Devices)

Android Beam允许两个设备之间简单的点对点数据交换。应用程序必须在数据发送方的设备的前台,接收方要处于解锁状态。当发送设备离接收设备足够近的联系时,发送设备会呈现“点击发送”(Touch To Beam)的界面。用户可以选择发送或者不发送(到接收设备)。
注意:前台NDEF推送在API 10才支持,提供了跟Android Beam相类似的功能。这些API被弃用,但仍然在较老设备上被支持。更多信息参阅 enableForegroundNdefPush() .

你能在应用中通过调用下面两个方法中的一个使得Android Beam有效:
  • setNdefPushMessage():( Accepts an NdefMessage to set as the message to beam)。当两设备足够靠近的时候自动发送消息。
  • setNdefPushMessageCallback():接收一个包含createNdefMessage() 并当消息接收设备在范围内就会调用 该方法的回调。这回调让你在需要的时候才创建NDEF消息。

一个Activity一次只能推送一条NDEF消息,所以当两个都被设置的时候setNdefPushMessageCallback()会优先于setNdefPushMessage()。为了使用Android beam,下面的常规指导必须阅读:

  • 发送数据的activity必须处于前台。所有设备都必须处于解锁状态。
  • 你必须将你要发送的信息封装到 NdefMessage 对象里。
  • 数据接收设备必须支持 com.android.npp 的NDEF推送协议或者NFC论坛的SNEP(Simple NDEF Exchange Protocol:简单NDEF交换协议)。com.android.npp 协议要求设备在API 9(Android 2.3)到API 13(Android 3.2)。com.android.npp 和 SNDP都要求API 14(Android 4.0)或以上。
注意:如果你的应用是能够Android Beam并且在前台,那么标准的intent分发系统将失效。然而,如果你的应用同时支持 前台分发( foreground dispatching),那么它仍然能在前台调度扫描与intent-filter匹配的tag。

为了使用Android Beam:
  1. 创建一个包含你想推送到其他设备的 NdefRecord  NdefMessage 
  2. 通过一个NdefMessage调用 setNdefPushMessage(),或者在你的 activity的onCteate()方法中通过一个NfcAdapter.CreateNdefMessageCallback的对象调用 setNdefPushMessageCallback。这些方法要求至少一个activity(that you want to enable with Android Beam),伴随着被激活的其它activity的可选列表。
        综上所述,如果在两个设备在交流范围内时,你的Activity总是发送同样的NDEF消息,那你通常使用 setNdefPushMessage() 就可以。如果你的应用关注当前应用的context并且想根据当前用户所做的来决定推送的NDEF消息,那么使用setNdefPushMessageCallback

接下来的例子展示了一个简单的activity在onCreate()中调用 NfcAdapter.CreateNdefMessageCallback(完整的AndroidBeamDem例子在这里)。这个例子也有方法帮助你创建MIME记录:
  1. package com.example.android.beam;
  2. import android.app.Activity;
  3. import android.content.Intent;
  4. import android.nfc.NdefMessage;
  5. import android.nfc.NdefRecord;
  6. import android.nfc.NfcAdapter;
  7. import android.nfc.NfcAdapter.CreateNdefMessageCallback;
  8. import android.nfc.NfcEvent;
  9. import android.os.Bundle;
  10. import android.os.Parcelable;
  11. import android.widget.TextView;
  12. import android.widget.Toast;
  13. import java.nio.charset.Charset;
  14. public class Beam extends Activity implements CreateNdefMessageCallback {
  15. NfcAdapter mNfcAdapter;
  16. TextView textView;
  17. @Override
  18. public void onCreate(Bundle savedInstanceState) {
  19. super.onCreate(savedInstanceState);
  20. setContentView(R.layout.main);
  21. TextView textView = (TextView) findViewById(R.id.textView);
  22. // Check for available NFC Adapter
  23. mNfcAdapter = NfcAdapter.getDefaultAdapter(this);
  24. if (mNfcAdapter == null) {
  25. Toast.makeText(this, "NFC is not available", Toast.LENGTH_LONG).show();
  26. finish();
  27. return;
  28. }
  29. // Register callback
  30. mNfcAdapter.setNdefPushMessageCallback(this, this);
  31. }
  32. @Override
  33. public NdefMessage createNdefMessage(NfcEvent event) {
  34. String text = ("Beam me up, Android!\n\n" +
  35. "Beam Time: " + System.currentTimeMillis());
  36. NdefMessage msg = new NdefMessage(
  37. new NdefRecord[] { createMime(
  38. "application/vnd.com.example.android.beam", text.getBytes())
  39. /**
  40. * The Android Application Record (AAR) is commented out. When a device
  41. * receives a push with an AAR in it, the application specified in the AAR
  42. * is guaranteed to run. The AAR overrides the tag dispatch system.
  43. * You can add it back in to guarantee that this
  44. * activity starts when receiving a beamed message. For now, this code
  45. * uses the tag dispatch system.
  46. */
  47. //,NdefRecord.createApplicationRecord("com.example.android.beam")
  48. });
  49. return msg;
  50. }
  51. @Override
  52. public void onResume() {
  53. super.onResume();
  54. // Check to see that the Activity started due to an Android Beam
  55. if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(getIntent().getAction())) {
  56. processIntent(getIntent());
  57. }
  58. }
  59. @Override
  60. public void onNewIntent(Intent intent) {
  61. // onResume gets called after this to handle the intent
  62. setIntent(intent);
  63. }
  64. /**
  65. * Parses the NDEF Message from the intent and prints to the TextView
  66. */
  67. void processIntent(Intent intent) {
  68. textView = (TextView) findViewById(R.id.textView);
  69. Parcelable[] rawMsgs = intent.getParcelableArrayExtra(
  70. NfcAdapter.EXTRA_NDEF_MESSAGES);
  71. // only one message sent during the beam
  72. NdefMessage msg = (NdefMessage) rawMsgs[0];
  73. // record 0 contains the MIME type, record 1 is the AAR, if present
  74. textView.setText(new String(msg.getRecords()[0].getPayload()));
  75. }
  76. }
注意你可以移除在AAR之外的代码注释。如果你使AAR生效了,那么被AAR指定的应用总是能收到Android Beam的消息。如果应用没有被呈现,谷歌商店会去下载这个应用。因此,下面的intent-filter在Android 4.0或之后使用AAR不是技术上必要的:
  1. <intent-filter>
  2. <action android:name="android.nfc.action.NDEF_DISCOVERED"/>
  3. <category android:name="android.intent.category.DEFAULT"/>
  4. <data android:mimeType="application/vnd.com.example.android.beam"/>
  5. </intent-filter>
通过这个intent-filter,com.example.android.beam 这个应用能够在扫描到NFC tag或者接收到com.example.android.beam这种AAR的Android Beam发送的消息,或者当一个NDEF格式的消息包含MIME类型为application/vnd.com.example.android.beam 的记录。

尽管AAR保证一个应用被启动或者下载,intent-filter仍然是被推荐的,因为他们让你启动一个你的应用程序中你选择的Activity,而不是总是启动被AAR指定包名的应用的mian Activity。AAR没有Activity级别区分。因此,一些安卓设备不支持AAR,为了以防万一,你应该在你的NDEF消息的第一条NDEF记录中嵌入识别信息,也为了过滤。如何创建记录请参阅创建常规类型的NDEF记录(Creating Common Types of NDEF records.




































0 0