NFC Basics
关于收发NDEF格式的消息,以及相关的api。非NDEFdata以及其他更深入技术请参见Advanced NFC。
在android端使用NDEF有两种情况:
- 从标签读取NDEF数据
- 从另个android手机中使用androidBeam来获取信息。
android使用标签派发系统来处理读取NDEF数据的情况,通过分析发现的标签,给数据分类,然后启动一个关注这个分类数据的app。应用程序可以通过注册Intent过滤器(Intent Filter)来获取关注的标签信息。
AndroidBeam功能可以让用户给另一台设备推送NDEF消息,仅仅通过贴靠一下两台设备就可以完成。这让无线交互变得比蓝牙一类的更加容易,因为使用NFC不需要手动查找设备或者配对。当两个设备靠近的时候通信就连接了。AndroidBeam可以通过一系列NFC api,所以应用都可以通过通过它在设备间交换信息。比如说联系人,浏览器,youtube客户端可以使用androidbeam来分享联系人,网页,或者视频。
标签分发系统
android设备在屏幕解锁的时候才查看NFC标签,除非NFC在设置中被关闭了。当安卓设备发现一个Tag时候,不会询问用户来选择app,而是根据intent来匹配最合适的app。因为NFC表标签的扫描是一个很短的过程,让用户选择app会使得设备离开标签而断开连接。你应该让你的activity直接处理它关注的标签,而不要弹出activity选择的菜单。
为了实现这个目标,android提供了一个特殊的标签分发系统,它可以扫描标签以后转化它们,然后定位匹配的app。具体是这样做的:
1. 解析标签并获得Tag的MIME类型, 或者引导数据的URI。
2.MIME类型和引导数据的URI封装成一个intent。前两个步骤在How NFC Tags are mapped to Applications介绍。
3.根据intent启动一个activity,这个的细节在How NFC Tags are Dispatched to Applications。
NFC标签是如何映射为MIME类型或者URI的(How NFC Tag are mapped to MIME and URIs)
当你开始编程之前,你应该清楚不同的标签的种类,标签派发系统怎样解析NFC标签,以及标签派发系统在探测到NDEF标签的时候所做的工作。NFC标签中包含了很广的技术,并且数据也是以不同的方式写入的。NDEF标准是android最支持的标准,制定者为NFC Forum。(NFC论坛)
NDEF数据别封装到一个了叫做NdefMessage的消息中,其中包含了很多的记录,叫做NdefRecord。每一个NDED记录都应该遵循你要创建的记录的类型的格式。android也支持其他不使用NDEF格式的数据标签,你可以在android.nfc.tech包中的类来帮助你处理这种标签。这些技术在Advanced NFC中介绍。我们推荐您使用NDEF标准,因为这样您可以更轻松地开发,并且获得android设备的最大的支持,如果使用其他的格式,您需要自己定义协议栈来和标签通信。
提示:更多的NEDF说明,请参考NFC Forum Specification Download 网站,然后查看 Create common types of NDEF records,里面有创建NDEF记录的例子。
现在你已经有一些了解了,下面接收android怎样处理 NDEF格式的标签。当android设备扫描到包含NDEF格式数据的标签的 时候,它会解析出其中的消息,并且获取MIME类别和URI。系统会读取NdefMessage中的第一个NdefRecord,来决定如何解析整个的NdefMessage(NdefMessage中有多个NedfRecord)。在一个典型的NDEF格式消息里面,第一个NdefRecord包含以下几个字段:
3-bit TNF(Type Name Format 类型名格式)
指明了解析变量边长类型字段(varible length field)的方式。数值对应的表格参见Table1。
Variable length type(变长字段类型)
描述了记录的类型。如果上面使用了TNF)_WELL_KNOWN,那么这里就指明RTD。RTD数值对应的情况请看表2。
变长字段的ID
关于纪律的特别指示。这个字段并不常用,如果你需要特别的指明一个标签,你可以为它创建一个ID。
变长字段
你真正想要读取或者写入的数据。一个Ndef message包含很多的Ndfe Record,所以不要以为全部数据都在第一个record里面。
标签派发系统通过TNF和类型字段来吧MINE类型或者URI映射为NDEF消息。如果成功了,就会把获得的信息和负载数据封装到一个ACTION_NDEF_DISCOVERED的里面。但是有些时候,仅仅靠第一个ndef Record是没法判断数据类型的。当MIME类型和URI没法映射为NDEF数据的时候,或者NFC标签不是以NDEF数据开头的。这种情况下,会产生一个Tag对象,其中包含了标签的技术信息和数据负载,然后Tag对象被封装到了一个ACTION_TECH_DISCOVERED的intent中,而不再产生上面提到的那种Intent。
表1描述了标签派法系统是怎样把TNF和类型字段映射为MIME类型或者URI的。里面同样描述了哪些的TNF不能被映射为MIME类型或者URI。这时标签派法系统就会会退到那个ACTION_TECH_DICOVERED的情况。
比如标签派发系统遇到了一个TNF_ABSOLUTE_URI的标签,就会把变长数据字段解释为一个URI。标签派发系统会把URI封装到一个ACTION_NDEF_DISCOVERED的intent的里面,其中还包含一些关于标签的信息,比如说数据负载。另一方面,如果遇到了TNF_UNKNOWN类型的记录,就会创建一个封装了标签技术的intent。
Table1 表1 支持的TNF以及它们的对应信息
Type Name Format (TNF) | Mapping |
---|---|
TNF_ABSOLUTE_URI |
URI based on the type field. |
TNF_EMPTY |
Falls back to 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 type based on the type field. |
TNF_UNCHANGED |
Invalid in the first record, so falls back to ACTION_TECH_DISCOVERED . |
TNF_UNKNOWN |
Falls back to ACTION_TECH_DISCOVERED . |
TNF_WELL_KNOWN |
MIME type or URI depending on the Record Type Definition (RTD), which you set in the type field. See Table 2. for more information on available RTDs and their mappings. |
Table 2. Supported RTDs for TNF_WELL_KNOWN and their mappings
Record Type Definition (RTD) | Mapping |
---|---|
RTD_ALTERNATIVE_CARRIER |
Falls back to ACTION_TECH_DISCOVERED . |
RTD_HANDOVER_CARRIER |
Falls back to ACTION_TECH_DISCOVERED . |
RTD_HANDOVER_REQUEST |
Falls back to ACTION_TECH_DISCOVERED . |
RTD_HANDOVER_SELECT |
Falls back to ACTION_TECH_DISCOVERED . |
RTD_SMART_POSTER |
URI based on parsing the payload. |
RTD_TEXT |
MIME type of text/plain . |
RTD_URI |
URI based on payload. |
NFC标签是如何派发给应用的
当标签标签派发系统已经创建了封装了关于标签的信息和指示信息的intent以后,就会把它传递给过滤这种intent的应用。如果多个应用都过滤(这里是要筛子上面的东西,可以理解为筛选,而不是漏出去的——小马)了这种intent,就会出现activity选择器供用户选择activity。标签派发系统定义三种类型的标签,下面按照优先级从高到低排序。
1. ACTION_NDEF_DISCOVERED: 这个intent用来在发现标签中包含NDEF格式的负载并且其中的类型是已知的情况下,启动一个activity。这种intent优先级最高,标签派发系统一旦可能就最先发出这种。
2.ACTION_TECH_DISCOVERED:如果没有应用注册捕获ACTION_NDEF_DISCOVERED的intent,那么系统就会启动这种intent。在NDEF类型的数据不能映射为MIME类型或者URI的时候,也会直接发出这种类型的intent(之前没有发出ndef那种),或者标签中不包含NDEF的数据而而是一种已知的标签技术,也会用这种intent。
3.ACTION_TAG_DISCOVERED:如果前两者intent么有被捕获,那么就启动这种intent。
标签派法系统基本工作方式如下:
1.解析NFC tag的时候发出一个intent来启动一个activity。(指的是CTION_NDEF_DISCOVERED和ACTION_TECH_DISCOVERED )。
2.如果没有activity捕获这个intent,就使用下一个优先级的intent来启动activity(ACTION_TECH_DISCOVERD或者ACTION_TAG_DISCOVERED),如果没有捕获就启动下一个优先级的 intent。
3.如过最后也没有 捕获,就什么也不做。
图1.标签派法系统
只要有可能,就启动NDEF的intent,因为这是三种之中最特殊的,这种intent可以让你更恰当地启动app,给用户更好的体验。
在Android Manifest中获取NFC访问
在你可以访问NFC硬件设备和处理NFCintent之前,在你的AndroidManifest.xml中声明如下:
- 访问NFC硬件的<uses-permission>元素
-
<uses-permission android:name="android.permission.NFC" />
- 你的应用支持的最小的sdk。api level 9仅仅通过ACTION_TAG_DISCOVERED支持有限的标签派发功能,通过EXTRA_NDEF_MESSAGES可以访问NDEF格式信息。其他的标签属性访问和IO操作都不支持。API level 10包含了广泛的读写操作的支持,还有前台的NDEF推送,API level14 提供了给其他设备推送NDEF消息的便捷办法,通过Android Beam和其他的创建NDEFrecords的便捷的方法。
<uses-sdk android:minSdkVersion="10"/>
- uses-feature 元素,在应用商店里面指示你的应用只能在NFC设备上面运行。
<uses-feature android:name="android.hardware.nfc" android:required="true" />
如果你的应用只是把nfc作为一个可选功能,那么你可以忽略uses-feature元素,并且在运行时候查看设备是是否支持nfc。比如通过方法getDefaultAdapter是不是空的。
筛选NFC Intent Filtering for NFC Intents
你的应用在扫描到NFC标签的时候启动,应该在你的应用中添加一种,两种,或者三种全部类型的intent筛选,就在Android Manifest中。然而通常都是倾向于把ACTION_NDEF_DISCOVERED作为启动你应用的选项。ACTION_TECH_DISCOVERED是第一种没有任何应用处理或者负载不是NDEF格式的时候的备选项。而使用ACTION_TAG_DISCOVERED则会导致筛选太宽泛。多数的应用都会按照优先级,先使用头两种,这样不会使得你的应用启动的太过频繁。TAG的intent作为最后的选择,在前面两种都没有应答的时候才使用。
因为NFC标签的部署种类很多样,而且也大都不在你的控制之下,所以这是你需要使用另外两种备选intent的原因。如果标签的写入部分在你的控制下,那么推荐你使用NDEF来作为标签的格式。下面的部分展示了怎样筛选不同类型的intent。
ACTION_NDEF_DISCOVERED
要筛选这个类型的intent,需要声明这个类型,和数据的类型下面这个例子就指定了ACTION_NDEF_DISCOVERED的筛选,和 text/plain的MIME类型。
<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
如果你的activity筛选了ACTION_TECCH_DISCOVERED intent,你必须同时使用一个 tech-list集来创建一个xml文件,指明你的应用支持哪些的技术类型。如果你的支持的技术集是标签中支持的技术的子集,那么就会匹配。你可以使用getTechList()来获取标签支持的类型。
举个例子,如果一个扫描的标签支持了MifareClassic,NdefFormatable,和NfcA,你的activity的筛选器的tech-list里面要支持三种中的至少一种。
下面是一个定义了所有的技术的tech-list的例子,你可以把你不需要的删掉。把这个文件存储在项目的根目录/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集合。每一个集合都作为独立的,你的任何的一个list中的技术是getTechList方法返回的标签中包含的技术的子集的时候,就认为匹配成功。这里面就是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>
标签技术和ACTION_TECH_DISCOVERED intent的更多的信息,请参加Working with Supported Tag Technologies,Advance NFC文档。
ACTION_TAG_DISCOVERED
这个标签的过滤器如下。
<intent-filter> <action android:name="android.nfc.action.TAG_DISCOVERED"/> </intent-filter>
从intent中获取信息 Obtaining information from intents
如果一个app是通过NFC intent启动的,那么可以从intent中获取扫描到的标签的信息。不同的标签可能包含以下的额外信息,extras。
- EXTRA_TAG(必有的):代表标签的Tag类对象。
- EXTRA_NDEF_MESSAGES(可选的):从标签中解析出来的NDEF message数组。在ACTION_NDEF_DISCOVERED的intent中是必须的。
- EXTRA_ID(可选):标签的底层的ID。
要先检查你应用是不是由NFC intent启动的,然后再从中获取这些额外信息。下面是一个检查 ACTION_NDEF_DISCOVERED 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记录 Creating Common Types of NDEF Records
这个部分讲述如何在你写入标签或者AndroidBeam通信的时候如何创建典型的NDEF记录。从Android 4.0,即API 14开始,你可以使用createUri来自动帮你创建URI类型。从Android4.1,即API 16开始,可以使用creatExternal或者createMine来创建MIME和其他的NDEF类型的record。尽量使用这些便捷的方式来避免手动创建NDEFrecord可能会发生的错误。
这部分还讲述了怎样创建与record对应的筛选器。当写入标签或者Android Beam时,所有NDEFrecord的例子都应该在NDEF message的第一个NDEF record中。
TNF_ABSOLUTE_URI
提示:我们推荐您使用更高效的RTD_URI来替代TNF_ABSOLUTE_URI。
可以使用下面的方法创建一个TNF_ABSOLUTE_URI NDEF record。
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]);
筛选这个intent的筛选器如下:
<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 record:
使用createMine()方法:
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 record的过滤器如下面这样。
<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
可以使用如下方法创建record:
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> <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
使用高级方法1
NdefRecord rtdUriRecord1 = NdefRecord.createUri("http://example.com");
使用高级方法2
Uri uri = new Uri("http://example.com"); NdefRecord rtdUriRecord2 = NdefRecord.createUri(uri);
手动创建
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> <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
使用高级方法:Using the createExternal()
method:
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);
手动创建:Creating the NdefRecord
manually:
byte[] payload; ... NdefRecord extRecord = new NdefRecord( NdefRecord.TNF_EXTERNAL_TYPE, "com.example:externalType", new byte[0], payload);
过滤器:
<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来适应更一般的NFC标签,这样可以更好的支持android设备和非android设备。
提示:TNF_EXTERNAL_TYPE的统一资源定位符有一个正式的格式: urn:nfc:ext:example.com:externalType,但是NFC Forum RTD说明书里面声明了: urn:nfc:ext: 的urn部分必须从NDEF record的格式中省略。所以你只需要提供一个域(比如说 例子中的example.com)和一个类型(比如说例子中的 externalType)就可以了,中间用冒号连接。当派发TNF_EXTERNAL_TYPE类型的时候,Android会将urn:nfc:ext:example.com:externalType的urn转化为vnd.android.nfc://ext/eample.com:externalType
的URI,就是例子中的intent过滤器中定义的那样。
Android应用记录 Android Application Records
AAR在Android4.0中引入,目的是为了在扫描到标签时保证app会被启动。AAR在NDEFrecord中嵌入了一个应用的包名。你可以把AAR加入到NDEF message的任何一个NDEFrecord当中,因为android会扫描NDEF message中的每一个NDEFrecord来寻找AAR。如果找到了就会基于AAR之中的包名来启动app。如果现在设备里面没有这个app,就运行GooglePlay来开始下载app。
AAR可以帮助你来防止其他的应用筛选同样的intent,并且可以让你的特定的标签。AAR只在app的级别被支持,因为使用了包名进行约束,而不是activity中的筛选器,如果想要activity界别的支持,请使用intent筛选器。
付过一个标签包含了AAR,标签派法系统按照下面的方式来运行:
1. 像之前一样用intent来启动一个activity,如果匹配了intent的activity也匹配了AAR,那么就启动Activity。
2.如果匹配了intent的acitivity的应用不匹配AAR,或者如果多个activity都可以拦截intent,或者如果没有Activity拦截这个intent,就启动AAR中指定的app。
3.如果没有应用可以匹配AAR,就去GooglePlay中下载在AAR中指定的application。
提示:你可以使用前台派发系统来覆盖AAR和intent派发系统,也就是说让一个前台的activity拥有获得NFC标签的优先级。使用这种方法,一个应用必须要在前台才能覆盖AAR和intent派发系统。
如果你还想使用filter来过滤不含有AAR的标签,你就可以想之前那样定义intent filter。这在你的应用想要捕获其他的不含有AAR的intent的时候很管用。举个例子,可能你会想保证让你的应用优先捕获AAR指定你的应用的intent,其次还想捕获其他感兴趣的第三方标签。记住AAR只在4.0和以后的版本中支持,所以在部署你的标签的时候,使用AAR和MIME类型/URI结合的方式,可以让你的标签被更多种类的设备支持。另外,部署标签的时候,思考怎样可以支持更多的设备是很重要的,比如说android设备和非安卓的设备。你可以通过定义一个相关的特别的MIME类型或者URI来让app更容易别识别出来。
Android 提供了一个创建AAR的简便的方法,createApplicationRecord()。你需要咬做的就是就是在你的NdefMessage到处嵌入AAR。除非AAR是你的NedfMessage中的唯一的record,否则不要把AAR放在NdefMessage的第一个record。这是因为android系统第一个记录来判断MIME类型和标签的URI。MIME和URI是用来产生给过滤器的intent的。下面是一个创建AAR的例子:
NdefMessage msg = new NdefMessage( new NdefRecord[] { ..., NdefRecord.createApplicationRecord("com.example.android.beam")}
向其他设备发送(Beam)NDEF消息 Beaming NDEF Messages to Other Devices
AndroidBeam可以让两个Android设备传送P2P的消息。发起Beam的应用一定要在设备的前台,而接收Beam的设备应该解锁屏幕。当发起设备和接收设备的距离足够近的时候,发起的 设备会出现“点击开始Beam传送的提示”。使用者可以选择是不是Beam给接收设备信息。
提示:前台的NDEF推送在API 10以上才可用,它的功能和Android Beam类似。这个API现在已经被弃用了,但是在老设备上支持。更多请参见enableForegoundNdefPush方法。
你可以用以下两种方法来开启你的Android Beam:
- setNdefPushMessage:接收一个NdefMessage对象作为传送的消息。在两个设备足够接近的时候会自动启动传送消息。
- setNedfPushiMessageCallback():接收一个包含createNdefMessage的回调,createNdefMessage就是上面的那个。 回调方法让你只有在必要的时候才创建NDEF message。
一个设备一次只能推送一个NDEF message,所以当两个都是设置的时候,CallBack方法比前者有更高的优先级。使用Android Beam你还要知道下面几个经验:
- 主动设备的activity要前台,被动设备要解锁。
- 只能使用NdefMessage封装你的消息。
- 被动设备要支持 com.android.npp NDEF推送协议,或者 NFC Forum的SNEP(简单 NDEF交换协议)。android2.3 api9到andro3.2 api13需要 com.android.npp协议,在4.0 api14以后com.android.npp和SNEP两种都需要。
提示:如果你的支持Android Beamactivity在前台,你的标准的intent派发系统就会停用。如果你的activity也开启了“前台派发”,那么仍然可以捕获在“前台派发”中设置的intent过滤器。
要使用Android Beam的步骤如下:
1.创建一个包换你想推送的NdefRecord的Ndef Message。
2.在你的activity的onCreate方法中调用setNdefPushMessage方法并传入NdefMessage参数,或者调用setNdefPushMessageCallback方法,传入NfcAdaper.CreateNdefMessgeCallback对象。在你想要使用AndroidBeam的至少一个activity中,你应该调用这些方法,并且可以选择附上一个其他想激活功能的activity的可选的列表。
通常来讲,如果你想在每次都推送相同的NDEF 方法,你可以使用setNdefPushMessage()方法。当你的应用要根据当前的上下文来决定推送什么样的消息的时候,就应该使用setNdefPushMessageCallback方法,这样会更加依赖于你的用户在应用中正在做什么。
下面是一个简单的调用NfcAdapter.CreateNdefMessageCallback的activity的例子。在activity中onCreate方法中调用,可以在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的应用就会接到AndroidBeam消息。 如果现在没有这个应用就会从Google Play中下载。这样的话,在技术上讲,对于Android4.0以后的设备使用AAR就没有必要使用下面的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>
使用这个intent过滤器,com.example.android.beam应用可以在以下情况的时候启动。当扫描到一个NFC标签或者收到了一个包含AAR com.example.android.beam 类型的Android Beam,或者一个NDEF格式的消息中包含了application/vnd.com.example.android.beam类型的 MIME记录。
进骨干AAR保证了应用被启动或者被下载,但是仍然推荐intent filter。因为这样可以启动一个activity而不是一个app中的主activity。AAR没有activity级别的标志。并且还有一些设备不支持AAR,你应该仍然在NDEFmessage的第一个NDEFRecord中嵌入指示信息,并且用过滤器来获取,以防万一。创建record的方法详情请参见Creating common Types fo NDEF records。