Android近场通信---NFC基础79

来源:互联网 发布:淘宝卖家怎么发快递 编辑:程序博客网 时间:2024/06/06 07:29

http://3y.uu456.com/bp_8phma7pqlv9pugm7q9tk_1.html

Android近场通信---NFC基础;分类:学习笔记;本文介绍在Android系通过你所能执行的基本任;NDEF数据和Android一起工作的场景主要有;1.从NFC标签中读取NDEF数据;;2.把NDEF消息从一个设备发送给另一个设备?;从NFC标签中读取NDEF数据是用标签调度系统来;AndroidBeam?功能允许设备把一个NDE;NFC标签调度系统


Android近场通信---NFC基础

分类: 学习笔记

本文介绍在Android系通过你所能执行的基本任务?它解释了如何用NDEF消息格式来发送和接收NFC数据,并且介绍了支持这些功能的Android框架API?有关更高级的话题,包括对非NDEF格式数据的讨论,情况“高级 NFC”

NDEF数据和Android一起工作的场景主要有两个:

1. 从NFC标签中读取NDEF数据;

2. 把NDEF消息从一个设备发送给另一个设备?

从NFC标签中读取NDEF数据是用标签调度系统来处理的,它会分析被发现的NFC标签,对数据进行适当的分类,并启动对该类数据感兴趣的应用程序?想要处理被扫描到NFC标签的应用程序会声明一个Intent过滤器,并请求处理数据?

Android Beam? 功能允许设备把一个NDEF消息推送到物理上相互监听的另一个设备上?这种交互提供了比其他无线技术(如蓝牙)更容易的发送数据的方法?因为NFC不需要手动的设备发现或配对要求?两个设备在接近到一定范围时会自动的连接?Android Beam通过一组NFC API来使用,以便应用程序能够在设备之间来传输信息?例如,通信录?浏览器以及YouTube等应用程序都使用Android Beam来跟其他设备共享通信录?网页和视频?

NFC标签调度系统

通常,除非是在设备的设置菜单中NFC被禁用,否则Android设备会在非锁屏的状态下搜索NFC?当Android设备发现NFC标签时,期望的行为是用最合适的Activity来处理该Intent,而不是询问用户使用什么应用程序?因为设备只能在很短的范围内扫描到NFC标签,强制的让用户手动的选择一个Activity,会导致设备离开NFC标签,从而中断该连接?你应该开发你自己的Activity来处理你所关心的NFC标签,从而阻止 选择器的操作?

为了帮助你达到这个目标,Android提供了特殊的标签调度系统,来分析扫描到的NFC标签,通过解析数据,在被扫描到的数据中尝试找到感兴趣的应用程序,具体做法如下:

1. 解析NFC标签并搞清楚标签中标识数据负载的MIME类型或URI;

2. 把MIME类型或URI以及数据负载封装到一个Intent中?

3. 基于Intent来启动Activity?

怎样把NFC标签映射到MIME类型和URI

开始编写NFC应用程序之前,重要的是要理解不同类型的NFC标签?标签调度系统是如何解析NFC标签的?以及在检测到NDEF消息时,标签调度系统所做的特定的工作等?NFC标签涉及到广泛的技术,并且有很多不同的方法向标签中写入数据?Android支持由NFC Forum所定义的NDEF标准?

NDEF数据被封装在一个消息(NdefMessage)中,该消息中包含了一条或多条记录(NdefRecord)?每个NDEF记录必须具有良好的你想要创建的记录类型的规范的格式?Android也支持其他的不包含NDEF数据类型的标签,你能够使用android.nfc.tech包中的类来工作?要使用其他类型标签来工作,涉及到编写自己的跟该标签通信的协议栈,因此我们建议你尽可能的使用NDEF,以便减少开发难度,并且最大化的支持Android设备?

3. 如果没有应用程序来处理任何类型的Intent,那么就不做任何事情。

图1.标签调度系统

在可能的情况下,都会使用NDEF消息和ACTION_NDEF_DISCOVERED类型的Intent来工作,因为它是这三种Intent中最标准的。这种Intent与其他两种Intent相比,它会允许你在更加合适的时机来启动你的应用程序,从而给用户带来更好的体验。

在Android的Manifest中申请NFC访问

在访问设备的NFC硬件和正确的处理NFC的Intent之前,要在AndroidManifest.xml文件中进行以下声明:

1. 在<uses-permission>元素中声明访问NFC硬件:

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

2. 你的应用程序所支持的最小的SDK版本。API Level 9只通过

ACTION_TAG_DISCOVERED来支持有限的标签调度,并且只能通过

EXTRA_NDEF_MESSAGES来访问NDEF消息。没有其他的标签属性或I/O操作可用。API Level 10中包含了广泛的读写支持,从而更好的推动了NDEF的应用前景,并且API Leve 14用Android Beam和额外的方便的创建NDEF记录的方法,向外提供了更容易的把NDEF消息推送给其他设备的方法。

<uses-sdkandroid:minSdkVersion="10"/>

3. 使用uses-feature元素,在Google Play中,以便你的应用程序能够只针对有NFC硬件的设备来显示。

<uses-featureandroid:name="android.hardware.nfc"android:required="true"/>

如果你的应用程序使用了NFC功能,但是相关的功能又不是你的应用程序的关键功能,你可以忽略uses-feature元素,并且要在运行时通过调用getDefaultAdapter()方法来检查NFC是否有效。

过滤NFC的Intent

要在你想要处理被扫描到的NFC标签时启动你的应用程序,可以在你的应用程序的Android清单中针对一种、两种或全部三种类型的NFC的Intent来过滤。但是,通常想要

在应用程序启动时控制最常用的ACTION_NDEF_DISCOVERED类型的Intent。在没有过滤ACTION_NDEF_DISCOVERED类型的Intent的应用程序,或数据负载不是NDEF时,才会从ACTION_NDEF_DISCOVERED类型的Intent回退到

ACTION_TECH_DISCOVERED类型的Intent。通常ACTION_TAB_DISCOVERED是最一般化的过滤分类。很多应用程序都会在过滤ACTION_TAG_DISCOVERED之前,过滤ACTION_NDEF_DISCOVERED或ACTION_TECH_DISCOVERED,这样就会降低你的应用程序被启动的可能性。ACTION_TAG_DISCOVERED只是在没有应用程序处理

ACTION_NDEF_DISCOVERED或ACTION_TECH_DISCOVERED类型的Intent的情况下,才使用的最后手段。

因为NFC标签的多样性,并且很多时候不在你的控制之下,因此在必要的时候你要回退到其他两种类型的Intent。在你能够控制标签的类型和写入的数据时,我们建议你使用NDEF格式。下文将介绍如何过滤每种类型的Intent对象。

ACTION_NDEF_DISCOVERED

要过滤ACTION_NDEF_DISCOVERED类型的Intent,就要在清单中跟你想要过滤的数据一起来声明该类型的Intent过滤器。以下是过滤text/plain类型的MIME的ACTION_NDEF_DISCOVERED类型过滤器的声明:

<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_DISCOVERED类型的Intent,你必须创建一个XML资源文件,该文件在tech-list集合中指定你的Activity所支持的技术。如果tech-list集合是标签所支持的技术的一个子集,那么你的Activity被认为是匹配的。通过调用getTechList()方法来获得标签所支持的技术集合。

例如,如果扫描到的标签支持MifareClassic、NdefFormatable和NfcA,那么为了跟它们匹配,tech-list集合就必须指定所有这三种技术,或者指定其中的两种或一种。

以下示例定义了所有的相关的技术。你可以根据需要删除其中一些设置。然后把这个文件保存到<project-root>/res/xml文件夹中(你能够把命名为任何你希望的名字):

2



<resourcesxmlns:xliff;<tech-list>;<tech>android.nfc.;<tech>android.nfc.;<tech>android.nfc.;<tech>android.nfc.;<tech>android.nfc.;<tech>


<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就被认为是匹配的。下列示例能够跟支持NfcA和Ndef技术NFC标签或者跟支持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_TAG_DISCOVERED

使用下列Intent过滤器来过滤ACTION_TAG_DISCOVERED类型的Intent:

<intent-filter>

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

</intent-filter>

从Intent中获取信息

如果因为NFC的Intent而启动一个Activity,那么你就能够从Intent中获取被扫描到的NFC标签的相关信息。根据被扫描到的标签,Intent对象能够以下额外的信息:

1. EXTRA_TAG(必须的):它是一个代表了被扫描到的标签的Tag对象;

2. EXTRA_NDEF_MESSAGES(可选):它是一个解析来自标签中的NDEF消息的数组。这个附加信息是强制在Intent对象上的;

3. {@link android.nfc.NfcAdapter#EXTRA_ID(可选):标签的低级ID。

要获取这些附加信息,就要确保你的Activity是被扫描到的NFC的Intent对象启动的,然后才能获得Intent之外的附加信息。下例检查ACTION_NDEF_DISCOVERED类型的Intent,并从Intent对象的附加信息中获取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发送数据。从Android4.0(API Level14)开始,可以用createUri()方法来帮助你自动的创建URI记录。从Android4.1(API Level 16)开始,可以用createExternal()和createMime()方法来帮助你创建MIME和外部类型的NDEF记录。使用这些辅助方法会尽可能的避免手动创建NDEF记录的错误。

本节还要介绍如何创建NDEF记录对应的Intent过滤器。所有的这些写入或发送到NFC标签的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]);

对应的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记录。

使用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"))); 对应的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>

TNF_WELL_KNOWN和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过滤器如下:

<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_KNOW和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过滤器如下:

<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类型的记录。

使用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过滤器如下:

<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包含以下格式:


3



urn:nfc:ext:example.com.;urn:nfc:ext:example.com:;vnd.android.nfc://ext/ex;Android应用程序记录(AndroidApp;在Android4.0(APILevel14)中;如果你想要防止其他的应用对相同的Intent的过;如果NFC标签中包含了AAR,则NFC标签调度系;1.通常,


urn:nfc:ext:example.com.externalType,但是,NFC论坛的RTD规范声明,URN的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应用程序记录(Android Application Record---AAR)

在Android4.0(API Level 14)中引入的Android应用程序记录(AAR),提供了较强的在扫描到NFC标签时,启动应用程序的确定性。AAR有嵌入到NDEF记录内部的应用程序的包名。你能够把一个AAR添加到你的NDEF消息的任何记录中,因为Android会针对AAR来搜索整个NDEF消息。如果它找到一个AAR,它就会基于AAR内部的包名来启动应用程序。如果该应用程序不在当前的设备上,会启动Google Play来下载对应的应用程序。

如果你想要防止其他的应用对相同的Intent的过滤并潜在的处理你部署的特定的NFC标签,那么AAR是有用的。AAR仅在应用程序级被支持,因为包名的约束,并不能在Activity级别来过滤Intent。如果你想要在Activity级处理Intent,请使用Intent过滤器。

如果NFC标签中包含了AAR,则NFC标签调度系统会按照下列方式来调度:

1. 通常,尝试使用Intent过滤器来启动一个Activity。如果跟该Intent匹配的Activity也跟AAR匹配,那么就启动该Activity。

2. 如果跟Intent队形的Activity跟AAR不匹配,或者是有多个Activity能够处理该Intent,或者是没有能够处理该Intent的Activity存在,那么就启动由AAR指定的应用程序。

3. 如果没有跟该AAR对应的应用程序,那么就会启动Google Play来小组基于该AAR的应用程序。

注意:你能够用前台调度系统来重写AAR和Intent调度系统,在NFC标签被发现时。它允许优先使用前台的Activity。用这种方法,Activity必须是在前台来重写AAR和Intent调度系统。

如果你依然想要过滤扫描到的没有包含AAR的NFC标签,通常,你能够声明Intent过滤器。如果你的应用程序对不包含AAR的其他NFC标签感兴趣,这种做法是有用的。例如,你可能想要保证你的应用程序处理你部署的专用NFC标签,以及由第三方部署的普通的NFC标签。要记住AAR是在Android4.0以后才指定的,因此部署NFC标签时,你很可能希望使用能够广泛支持AAR和MIME类型/URI的是设备。另外,在你部署NFC标签时,还要想如何编写你的NFC标签,以便让大多数设备(Android设备和其他设备)支持。同过定义相对唯一的MIME类型或URI,让应用程序更容易的区分,就可以做到这一点。

Android提供了简单的创建AAR的API:createApplicationRecord()。你需要做的所有工作就是把AAR嵌入到你的NdefMessage中。除非AAR是NdefMessage中的唯一

记录,否则不要把使用NdefMessage的第一条记录。这是因为,Android系统会检查NdefMessage的第一条记录来判断NFC标签的MIME类型或URI,这些信息被用于创建对应应用程序的Intent对象。以下代码演示了如何创建一个AAR:

NdefMessage msg = new NdefMessage(

new NdefRecord[] {

...,

NdefRecord.createApplicationRecord("com.example.android.beam")}

把NDEF消息发射到其他设备上

Android Beam允许在两个Android设备之间进行简单的对等数据交换,想要把数据发送给另一个设备的应用程序必须是在前台,并且接收数据的设备必须不被锁定。当发射设备跟接收设备的距离足够近的时候,发射设备会显示“Touch to Beam(触摸发射)”的UI。然后,用户能够选择是否把消息发射给接收设备。

注意:在API Level 10中可以利用前台的NDEF推送,它提供了与Android Beam类似的功能。这些API已经过时了,但是在一些老旧设备上还有效。更多的信息请看enableForegroundNdefPush()。

通过调用下列两个方法中的任意一个,就能够为你的应用程序启用Android Beam:

1. setNdefPushMessage():这个方法把接收到的NdefMessage对象作为一个消息设置给Beam。当两个设备足够近的时候,就会自动的发送消息。

2. setNdefPushMessageCallback():接收包含createNdefMessage()方法的回调,当设备在发射数据的范围内时,这个回调方法会被调用。回调会让你只在需要的时候创建NDEF消息。

一个Activity一次只能推送一条NDEF消息,因此如果同时使用了这两种方法,那么setNdefPushMessageCallback()方法的优先级要高于setNdefPushMessage()方法。要使用Android Beam,通常必须满足以下条件:

1. 发射数据的Activity必须是在前台。两个设备的屏幕都必须没有被锁定;

2. 必须发要发射的数据封装到一个NdefMessage对象中;

3. 接收发射数据的NFC设备必须支持com.android.npp NDEF推送协议或是NFC组织的SNEP协议(简单的NDEF交换协议)。在API Level9(Android2.3)到API Level 13(Android3.2)的设备上需要com.android.npp协议。在API Level 14(Android4.0)和以后的设备上,com.android.npp和SNEP都需要。

注意:如果在前台的Activity启用了Android Beam,那么标准的Intent调度系统就会被禁用。但是,如果该Activity还启用了前台调度,那么在前台调度系统中,它依然能够扫描到跟Intent过滤器匹配的NFC标签。

启用Android Beam:

1. 创建一个准备推送到另一个设备上的包含NdefRecord的NdefMessage对象。

2. 调用带有NdefMessage类型参数的setNdefPushMessage()方法,或者是在Activity的onCreate()方法中调用setNdefPushMessageCallback方法来传递实现

NfcAdapter.CreateNdefMessageCallback接口的对象。这两个方法都至少需要一个准备要启用Android Beam的Activity,以及一个可选的其他的活跃的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 canadd it back in to guarantee that this

* activity starts when receiving a beamed message. For now, this code * uses thetag 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就会启动下载程序。因此,如果使用AAR,对于Android4.0以后的设备,下列的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应用就能够在以下情况下被启动:

1. 扫描到NFC标签;

2. 接收到com.example.android.beam类型的AAR或NDEF消息中包含一条

application/vnd.com.example.android.beam类型的MIME记录的Android beam的时候。

即使通过AAR能够保证了一个应用程序被启动或下载,但是还是推荐使用Intent过滤器,因为它会让你选择启动应用程序中Activity,而不是总启动AAR中指定的应用程序包的主Activity。AAR没有Activity级别的粒度。而且还有一些android设备不支持AAR,你还应该在NDEF消息的第一条NDEF记录中嵌入标识信息,以及对应的过滤器

Android近场通信---高级NFC

分类: 学习笔记

本文介绍一些高级的NFC专题,如多样的NFC标签技术、编写NFC标签、以及前台调度,前台调度允许在前台的应用程序优先调度Intent事件,即使还有其他的过滤同样的Intent事件的应用程序存在。

Android所支持的NFC标签技术

在使用NFC标签和Android设备来进行工作的时候,使用的读写NFC标签上数据的主要格式是NDEF。当设备扫描到带有NDEF的数据时,Android会提供对消息解析的支持,并在可能的时候,会以NdefMessage对象的形式来发送它。但是,有些情况下,设


4



3.通过调用android.nfc.tech包中;TagTechnology对象实例;读写NFC标签;读写NFC标签,要涉及到从Intent对象中获取;packagecom.example.andro;importandroid.nfc.Tag;;importandroid.nfc.tech.M;importandroid.util.Log;;importj


3. 通过调用android.nfc.tech包中对应类的一个get工厂方法,来获取一个

TagTechnology对象实例。在调用get工厂方法之前,通过调用getTechList()方法来枚举NFC标签所支持的技术。例如,用下列方法从Tag对象中获取MifareUltralight对象实例: MifareUltralight.get(intent.getParcelableExtra(NfcAdapter.EXTRA_TAG));

读写NFC标签

读写NFC标签,要涉及到从Intent对象中获取标签,并要打开与标签的通信。要读写NFC标签数据,你必须要定义自己的协议栈。但是,要记住在直接使用NFC标签工作时,你依然能够读写NDEF数据。这是你想要如何构建的事情。下例演示了如何使用MIFARE超薄标签来工作:

package com.example.android.nfc;

import android.nfc.Tag;

import android.nfc.tech.MifareUltralight;

import android.util.Log;

import java.io.IOException;

import java.nio.charset.Charset;

publicclassMifareUltralightTagTester{

privatestaticfinalString TAG =MifareUltralightTagTester.class.getSimpleName();

publicvoid writeTag(Tag tag,String tagText){

MifareUltralight ultralight =MifareUltralight.get(tag);

try{

ultralight.connect();

ultralight.writePage(4,"abcd".getBytes(Charset.forName("US-ASCII"))); ultralight.writePage(5,"efgh".getBytes(Charset.forName("US-ASCII"))); ultralight.writePage(6,"ijkl".getBytes(Charset.forName("US-ASCII"))); ultralight.writePage(7,"mnop".getBytes(Charset.forName("US-ASCII"))); }catch(IOException e){

Log.e(TAG,"IOException while closing MifareUltralight...", e);

}finally{

try{

ultralight.close();

}catch(IOException e){

Log.e(TAG,"IOException while closing MifareUltralight...", e);

}

}

}

publicString readTag(Tag tag){

MifareUltralight mifare =MifareUltralight.get(tag);

try{

mifare.connect();

byte[] payload = mifare.readPages(4);

returnnewString(payload,Charset.forName("US-ASCII"));

}catch(IOException e){

Log.e(TAG,"IOException while writing MifareUltralight

message...", e);

}finally{

if(mifare !=null){

try{

mifare.close();

}

catch(IOException e){

Log.e(TAG,"Error closing tag...", e);

}

}

}

returnnull;

}

}

使用前台调度系统

前台调度系统允许一个Activity拦截Intent对象,并且声明该Activity的优先级要比其他的处理相同Intent对象的Activity高。使用这个系统涉及到为Android系统构建一些数据结构,以便能够把相应的Intent对象发送给你的应用程序,以下是启用前台调度系统的步骤:

1. 在你的Activity的onCreate()方法中添加下列代码:

A. 创建一个PendingIntent对象,以便Android系统能够在扫描到NFC标签时,用它来封装NFC标签的详细信息。

PendingIntent pendingIntent =PendingIntent.getActivity(

this,0,newIntent(this,

getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP),0);

B. 声明你想要截获处理的Intent对象的Intent过滤器。前台调度系统会在设备扫描到NFC标签时,用声明的Intent过滤器来检查接收到的Intent对象。如果匹配就会让你的应用程序来处理这个Intent对象,如果不匹配,前台调度系统会回退到Intent调度系统。

如果Intent过滤器和技术过滤器的数组指定了null,那么就说明你要过滤所有的退回到TAG_DISCOVERED类型的Intent对象的标签。以下代码会用于处理所有的

NDEF_DISCOVERED的MIME类型。只有在需要的时候才做这种处理:

IntentFilter ndef =newIntentFilter(NfcAdapter.ACTION_NDEF_DISCOVERED); try{

ndef.addDataType("*/*"); /* Handles all MIME based dispatches. You should specify only the ones that you need. */ }

catch(MalformedMimeTypeException e){

thrownewRuntimeException("fail", e);

}

intentFiltersArray =newIntentFilter[]{ndef,};

C. 建立一个应用程序希望处理的NFC标签技术的数组。调用

Object.class.getName()方法来获取你想要支持的技术的类:

techListsArray = new String[][] { new String[] { NfcF.class.getName() } };

2. 重写下列Activity生命周期的回调方法,并且添加逻辑在Activity挂起(onPause())和获得焦点(onResume())时,来启用和禁用前台调度。

enableForegroundDispatch()方法必须在主线程中被调用,并且只有在该Activity在前台的时候(要保证在onResume()方法中调用这个方法)。你还需要实现onNewIntent回调方法来处理扫描到的NFC标签的数据:

publicvoid onPause(){

super.onPause();

mAdapter.disableForegroundDispatch(this);

}

publicvoid onResume(){

super.onResume();

mAdapter.enableForegroundDispatch(this, pendingIntent, intentFiltersArray, techListsArray);

}

publicvoid onNewIntent(Intent intent){

Tag tagFromIntent = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG); //do something with tagFromIntent

}

完整的示例请看API Demo中的ForegroundDispatch


0 0
原创粉丝点击