Android 2.3.3 近场通信NFC介绍
扫描二维码
随时随地手机看文章
NFC( 近场通信 )
NFC 是一套短距离的无线通信,通常距离是 4 厘米或更短。 NFC 工作频率是 13.56M Hz, 传输速率是 106kbit/s 到848kbit/s. NFC 总是在一个发起者和一个被动目标之间发生。发起者发出近场无线电波,这个近场可以给被动目标供电。这些被动的目标包括不需要电源的标签,卡,也可以是有电源的设备。
与其他无线通信技术比较, 例如蓝牙和 WiFi , NFC 提供更低贷款和距离,并且低成本,不需要供电,不需要实现匹配,整个通信过程仅仅是短短的靠近一秒就能完成。
一个带有 NFC 支持的 android 设备通常是一个发起者。也可以作为 NFC 的读写设备。他将检测 NFC tags 并且打开一个Activity 来处理 . Android 2.3.3 还有支持有限的 P2P 。
Tags 分很多种,其中简单的只提供读写段,有的只能读。复杂的 tags 可以支持一些运算,加密来控制对 tags 里数据段的读写。甚至一些 tags 上有简单的操作系统,允许一些复杂的交互和可以执行一些代码。
API 概览
Android.nfc package 包含顶层类用来与本地 NFC 适配器交互 . 这些类可以表示被检测到的 tags 和用 NDEF 数据格式。
android.nfc.techpackage 包含那些对 tag 查询属性和进行 I/O 操作的类。这些类分别标示一个 tag 支持的不同的NFC 技术标准。
声明 Android Manifest.xml 的元素
在你能访问一个设备的 NFC 硬件和正确的处理 NFC 的 Intent 之前,需要在 AndroidManifest.xml 中先声明下面的项:
1. NFC 使用 元素来访问 NFC 硬件 :
2. 最小 SDK 版本需要设置正确, API level 9 只包含有限的 tag 支持 , 包括:
. 通过 ACTION_TAG_DISCOVERED来发布 Tag 信息
. 只有通过 EXTRA_NDEF_MESSAGES扩展来访问 NDEF 消息
. 其他的 tag 属性和 I/O 操作都不支持
所以你可能想要用 API level 10 来实现对 tag 的广泛的读写支持。
3. uses-feature 元素定义:你的程序可以再 android 市场里显示有 NFC 硬件。
4. NFC intent filter 告诉 android 系统你的 activity 能处理 NFC 数据,可以定义 1 个或多个 intent filter :
android:resource = "@xml/nfc_tech_filter .xml" />
上边 3 个 intent filters 有优先级,更多信息可以看下面的 Tag 发布系统
也可以看 NFCDemo 例子的 AndroidManifest.xml 来有个更深的理解。
Tag 发布系统
当 android 设备扫描到一个 NFC tag ,通用的行为是自动找最合适的 Activity 会处理这个 tag Intent 而不需要用户来选择哪个 Activity 来处理。因为设备扫描 NFC tags 是在很短的范围和时间,如果让用户选择的话,那就有可能需要移动设备,这样将会打断这个扫描过程。你应该开发你只处理需要处理的 tags 的 Activity ,以防止让用户选择使用哪个 Activity来处理的情况。 Android 提供两个系统来帮助你正确的识别一个 NFC tag 是否是你的 Activity 想要处理的: Intent 发布系统和前台 Activity 发布系统。
Intent 发布系统检查所有 Activities 的 intent filters ,找出那些定义了可以处理此 tag 的 Activity ,如果有多个 Activity 都配置了处理同一个 tag Intent ,那么将使用 Activity 选择器来让用户选择使用哪个 Activity 。用户选择之后,将使用选择的 Activity 来处理此 Intent .
前台发布系统允许一个 Activity 覆盖掉 Intent 发布系统而首先处理此 tag Intent ,这要求你将要处理 Tag Intent 的 Activity运行在前台,这样当一个 NFC tag 被扫描到,系统先检测前台的 Activity 是否支持处理此 Intent ,如果支持,即将此Intent 传给此 Activity ,如果不支持,则转到 Intent 发布系统。
使用 Intent 发布系统
Intent 发布系统指定了 3 个 intent 有不同的优先级。通常当一个 tag 被检测到之后, Intent 就被启动( start )了,这个启动遵循以下行为 :
· android.nfc.action.NDEF_DISCOVERED : 这个 intent 是在一个包含 NDEF 负载的 tag 被检测到时启动,这是最高优先级的 intent, android 系统不会让你指定一个 Intent 能处理所有的 NFC 数据类型,你必须在AndroidManifest.xml 中指定与 NFC tag 对应的 元素,这样当扫描到的 tag 传过来的数据类型与你定义的相匹配时,你的 Activity 就会被调用。例如想处理一个包含 plain text 的 NDEF_DISCOVERED intent ,你要按照如下定义AndroidManifest.xml file:
如果 NDEF_DISCOVERED intent 已经被启动, TECH_DISCOVERED 和 TAG_DISCOVERED intents 将不会被启动。假如一个未知的 tag 或者不包含 NDEF 负载的 tag 被检测到,此 Intent 就不会被启动。
· android.nfc.action.TECH_DISCOVERED : 如果 NDEF_DISCOVERED intent 没启动或者没有一个 Activity 的filter 检测 NDEF_DISCOVERED ,并且此 tag 是已知的,那么此 TECH_DISCOVERED Intent 将会启动 .TECH_DISCOVERED intent 要求你在一个资源文件里 (xml) 里指定你要支持 technologies 列表。更多细节请看下面的Specifying tag technologies to handle .
· android.nfc.action.TAG_DISCOVERED : 如果没有一个 activity 处理 _DISCOVERED and TECH_DISCOVEREDintents 或者 tag 被检测为未知的,那么此 Intent 将会被启动。
Specifying tag technologies to handle 指定处理的 technologies
假如你的 Activity 在 AndroidManifest.xml 文件里声明了处理 android.nfc.action.TECH_DISCOVERED intent,你必须创建一个 Xml 格式的资源文件,并加上你的 activity 支持的 technologies 到 tech-list 集合里。这样你的 activity将被认作能处理这些 tech-list 的处理者,如果 tag 使用的 technology 属于你的定义的 list 里,你的 Activity 将接收此Intent 。你可以用 getTechList() 来获得 tag 支持的 technologies 。
例如:如果一个 tag 被检测到支持 MifareClassic, NdefFormatable, 和 NfcA ,你的 tech-list 集合必须指定了其中的一项或者多项来保证你的 Activity 能处理此 Intent 。
下面是一个资源文件例子,定义了所有的 technologies. 你可以根据需要删掉不需要的项,将此文件以任意名字 +.xml 保存到 /res/xml 文件夹 .
asis:names:tc:xliff:document:1.2" >
android.nfc.tech.IsoDep
android.nfc.tech.NfcA
android.nfc.tech.NfcB
android.nfc.tech.NfcF
android.nfc.tech.NfcV
android.nfc.tech.Ndef
android.nfc.tech.NdefFormatable
android.nfc.tech.MifareClassic
android.nfc.tech.MifareUltralight
你也可以指定多个 tech-list 集合,每个集合都认做独立的。如果任何单个 tech-list 集合是 getTechList() 返回的technologies 集合的子集,那么你的 Activity 将被认为匹配了。这个还提供 ’ 与 ’ 和 ’ 或 ’ 操作。下面的例子表示支持NfcA 和 NDef 的卡,或者支持 NfcB 和 NDef 的卡:
asis:names:tc:xliff:document:1.2" >
android.nfc.tech.NfcA
android.nfc.tech.Ndef
asis:names:tc:xliff:document:1.2" >
android.nfc.tech.NfcB
android.nfc.tech.Ndef
在 AndroidManifest.xml 文件中 , 指定这个 tech-list 资源文件的方法是在 元素中创建 元素,例如下面例子 :
...
android:resource = "@xml/nfc_tech_filter" />
...
使用前台发布系统 Using the foreground dispatch system
前台发布系统允许一个 Activity 拦截一个 tag Intent 获得最高优先级的处理,这种方式很容易使用和实现:
1. 添加下列代码到 Activity 的 onCreate() 方法里
a. 创建一个 PendingIntent对象 , 这样 Android 系统就能在一个 tag 被检测到时定位到这个对象
PendingIntent pendingIntent = PendingIntent . getActivity (
this , 0 , new Intent ( this , getClass ()). addFlags ( Intent .FLAG_ACTIVITY_SINGLE_TOP ), 0 );
b. 在 Intent filters 里声明你想要处理的 Intent ,一个 tag 被检测到时先检查前台发布系统,如果前台 Activity 符合Intent filter 的要求,那么前台的 Activity 的将处理此 Intent 。如果不符合,前台发布系统将 Intent 转到 Intent 发布系统。如果指定了 null 的 Intent filters ,当任意 tag 被检测到时,你将收到 TAG_DISCOVERED intent 。因此请注意你应该只处理你想要的 Intent 。
[!--empirenews.page--]
IntentFilter ndef = new IntentFilter ( NfcAdapter . ACTION_NDEF_DISCOVERED);
try {
ndef . addDataType ( "*/*" ); /* Handles all MIME based dispatches.
You should specify only the ones that you need. */
}
catch ( MalformedMimeTypeException e ) {
throw new RuntimeException ( "fail" , e );
}
intentFiltersArray = new IntentFilter [] {
ndef ,
};
c. 设置一个你程序要处理的 Tag technologies 的列表,调用 Object.class.getName() 方法来获得你想要支持处理的 technology 类。
techListsArray = new String [][] { new String [] { NfcF . class . getName () } };
2. 覆盖下面的方法来打开或关闭前台发布系统。比如 onPause() 和 onResume ()方法。必须在主线程里调用[url=http://developer.android.com/reference/android/nfc/NfcAdapter.html#enableForegroundDispatch%28android.app.Activity,%20android.app.PendingIntent,%20android.content.IntentFilter[],%20java.lang.String[][]%29]enableForegroundDispatch(Activity, PendingIntent, IntentFilter[], String[][])[/url]而且Activity 在前台(可以在 onResume() 里调用来保证这点)。你也要覆盖 onNewIntent 回调来处理得到的 NFC tag 数据。
public void onPause () {
super . onPause ();
mAdapter . disableForegroundDispatch ( this );
}
public void onResume () {
super . onResume ();
mAdapter . enableForegroundDispatch ( this , pendingIntent , intentFiltersArray, techListsArray );
}
public void onNewIntent ( Intent intent ) {
Tag tagFromIntent = intent . getParcelableExtra ( NfcAdapter . EXTRA_TAG );
//do something with tagFromIntent
}
See the ForegroundDispatch sample from API Demos for the complete sample.
使用 NFC tag 上的数据
NFC tag 上的数据是以字节存放,所以你可以将其转换成其他你想要的格式。当往 tag 写东西时,你必须以字节格式来写。 Android 提供 API 来帮助写符合 NDEF 标准的信息。使用此标准能保证你的数据在往 tag 写时能被所有 Android NFC 设备支持。然而,很多 tag 使用他们自己的标准来存储数据,这些标准也被 Android 支持。但你必须自己实现协议栈来读写这些 tag 。你可以在 android.nfc.tech 里找到所有支持的 technologies ,并且可以在 TagTechnology 接口里对technology 有个了解。这一段是简单介绍在 android 系统里怎样使用 NDEF 消息。这不意味着是一个完整的 NDEF 功能的介绍。但标出了主要需要注意和使用的东西。
为了方便使用 NDEF 消息, android 提供 NdefRecord和 NdefMessage来包装原始字节数据为 NDEF 消息。一个NdefMessage 是保存 0 个或多个 NdefRecords 的容器。每个 NdefRecord 有自己的唯一类型名字格式,记录类型和 ID 来与其他记录区分开。你可以存储不同类型的记录,不同的长度到同一个 NdefMessage 。 NFC tag 容量的限制决定你的NdefMessage 的大小。
那些支持 Ndef 和 NdefFormatable 技术的 tag 可以返回和接受 NdefMessage 对象为参数来进行读写操作。你需要创建你自己的逻辑来为其他在 android.nfc.tech 的 tag 技术实现读写字节的操作。
你可以从 NFC Forum(http://www.nfc-forum.org/specs/) 下载 NDEF 消息标准的技术文档,比如纯文本和智慧型海报 . NFCDemo 例子里声明了纯文本和智慧型海报的 NDef 消息。
读一个 NFC tag
当一个 NFC tag 靠近一个 NFC 设备,一个相应的 Intent 将在设备上被创建。然后通知合适的程序来处理此 Intent 。
下面的方法处理 TAG_DISCOVERED intent 并且使用迭代器来获得包含在 NDEF tag 负载的数据
NdefMessage [] getNdefMessages ( Intent intent ) {
// Parse the intent
NdefMessage [] msgs = null ;
String action = intent . getAction ();
if ( NfcAdapter . ACTION_TAG_DISCOVERED . equals ( action )) {
arcelable [] 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 ];
}
}
else {
// Unknown tag type
byte [] empty = new byte [] {};
NdefRecord record = new NdefRecord ( NdefRecord . TNF_UNKNOWN , empty , empty, empty );
NdefMessage msg = new NdefMessage ( new NdefRecord [] { record });
msgs = new NdefMessage [] { msg };
}
}
else {
Log . e ( TAG , "Unknown intent " + intent );
finish ();
}
return msgs ;
}
请记住 NFC 设备读到的数据是 byte 类型,所以你可能需要将他转成其他格式来呈现给用户。 NFCDemo 例子展示了怎样用 com.example.android.nfc.record 中的类来解析 NDEF 消息,比如纯文本和智慧型海报。
写 NFC tag
往 NFC tag 写东西涉及到构造一个 NDEF 消息和使用与 tag 匹配的 Tag 技术。下面的代码展示怎样写一个简单的文本到NdefFormatable tag :
NdefFormatable tag = NdefFormatable . get ( t );
Locale locale = Locale . US ;
final byte [] langBytes = locale . getLanguage (). getBytes ( Charsets . US_ASCII );
String text = "Tag, you‘re it!" ;
final byte [] textBytes = text . getBytes ( Charsets . UTF_8 );
final int utfBit = 0 ;
final char status = ( char ) ( utfBit + langBytes . length );
final byte [] data = Bytes . concat ( new byte [] {( byte ) status }, langBytes , textBytes);
NdefRecord record = NdefRecord ( NdefRecord . TNF_WELL_KNOWN , NdefRecord . RTD_TEXT , newbyte [ 0 ], data );
try {
NdefRecord [] records = { text };
NdefMessage message = new NdefMessage ( records );
tag . connect ();
tag . format ( message );
}
catch ( Exception e ){
//do error handling
}
点对点的数据交换
前台推送技术支持简单点对点的数据交换,你可以用 enableForegroundNdefPush(Activity, NdefMessage)方法来打开此功能 . 为了用这个功能:
· 推送数据的 Activity 必须是前台 Activity 。
· 你必须将你要发送的数据封装到 NdefMessage 对象里。
· 接收推送数据的设备必须支持 com.android.npp NDEF 推送协议,这个对于 Android 设备是可选的
假如你的 Activity 打开了前台推送功能并且位于前台,这时标准的 Intent 发布系统是禁止的。然而,如果你的Activity 允许前台发布系统,那么此时检测 tag 的功能仍然是可用的,不过只适用于前台发布系统。
要打开前台推送 :
1. 创建一个你要推送给其他 NFC 设备的包含 NdefRecords 的 NdefMessage 。
2. 在你的 Activity 里实现 onResume()和 onPause()的回调来正确处理前台推送的生命周期。你必须在你的Activity 位于前台并在主线程里调用 enableForegroundNdefPush(Activity, NdefMessage)(可以在onResume() 里调用来保证这点) .
public void onResume () {
super . onResume ();
if ( mAdapter != null )
mAdapter . enableForegroundNdefPush ( this , myNdefMessage );
}
public void onPause () {
super . onPause ();
if ( mAdapter != null )
mAdapter . disableForegroundNdefPush ( this );
}
当 Activity 位于前台,你可以靠近另外一个 NFC 设备来推送数据。请参考例子 ForegroundNdefPush 来了解点对点数据交换。