此文为我一时兴起花费将近两天翻译,肯定问题多多,慢慢完善,敬请谅解。
官网原文链接: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,解析他们,并尝试定位/找出与扫描数据最匹配的应用。大致是通过:
- 解析NFC tag并找出MIME类型或者标识一个tag有效负载的URI。
- 将MIME类型或者URI以及有效负载封装到一个intent。前两个步骤在How NFC tags are mapped to MIME types and URIs里描述了。
- 通过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类型,在追求尽可能简单的开发情况下,并且它被大多数安卓设备所支持。
记录:要下载完整的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和他们的映射。
表2.支持的TNF为TNF_WELL_KNOWN时的RTD和对应的映射。
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硬件:
<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记录的方法。
<uses-sdk android:minSdkVersion="10"/>
- uses-feature元素能让你的应用在谷歌商店中显示在有NFC硬件的设备上:
<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示例:
<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的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>
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路径下边。
<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集合。每一个都会被单独考虑,只要任何一个是
getTechList()的子集你的activity都会被考虑匹配。这里还提供了
AND 和
OR 运算来匹配技术。下面的例子可以匹配支持NfcA和Ndef或者MfcB和Ndef技术的tag。
<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_DISCOVERED的intent请到高级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>
得到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信息。
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记录(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记录:
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;
}
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);
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);
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 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:
NdefMessage msg = new NdefMessage(
new NdefRecord[] {
...,
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:
- 创建一个包含你想推送到其他设备的 NdefRecord 的 NdefMessage 。
- 通过一个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记录:
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的消息。如果应用没有被呈现,谷歌商店会去下载这个应用。因此,下面的intent-filter在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>
通过这个
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
)
.