Android开发者文章:近场通信---高级NFC编程
本文译自:http://developer.android.com/guide/topics/connectivity/nfc/advanced-nfc.html
本文介绍一些高级的NFC专题,如多样的NFC标签技术、编写NFC标签、以及前台调度,前台调度允许在前台的应用程序优先调度Intent事件,即使还有其他的过滤同样的Intent事件的应用程序存在。
Android所支持的NFC标签技术
在使用NFC标签和Android设备来进行工作的时候,使用的读写NFC标签上数据的主要格式是NDEF。当设备扫描到带有NDEF的数据时,Android会提供对消息解析的支持,并在可能的时候,会以NdefMessage对象的形式来发送它。但是,有些情况下,设备扫描到的NFC标签没有包含NDEF数据,或者该NDEF数据没有被映射到MIME类型或URI。在这些情况下,你需要打开跟NFC标签的通信,并用自己的协议(原始的字节形式)来读写它。Android用android.nfc.tech包提供了对这些情况的一般性支持,这个包在下表1中介绍。你能够使用getTechList()方法来判断NFC标签所支持的的技术,并且用android.nfc.tech提供的一个类来创建对应的TagTechnology对象。
表1.NFC标签所支持的技术
类 | 介绍 |
所有的NFC标签技术类必须实现的接口。 | |
提供对NFC-A(ISO 14443-3A)属性和I/O操作的访问。 | |
提供对NFC-B(ISO 14443-3B)属性和I/O操作的访问。 | |
提供对NFC-F(ISO 6319-4)属性和I/O操作的访问。 | |
提供对NFC-V(ISO 15693)属性和I/O操作的访问。 | |
提供对NFC-A(ISO 14443-4)属性和I/O操作的访问。 | |
提供对NDEF格式的NFC标签上的NDEF数据和操作的访问。 | |
提供了对可以被NDEF格式化的NFC标签的格式化操作。 |
表2.可选的NFC标签所支持的技术
类 | 介绍 |
如果Android设备支持MIFARE,那么它提供了对经典的MIFARE类型标签属性和I/O操作的访问。 | |
如果Android设备支持MIFARE,那么它提供了对超薄的MIFARE类型标签属性和I/O操作的访问。 |
NFC标签和ACTION_TECH_DISCOVERED类型的Intent协同工作
当设备扫描到带有NDEF数据的NFC标签,但却不能映射到MIME或URI时,NFC标签调度系统就尝试使用ACTION_TECH_DISCOVERED类型的Intent来启动一个Activity。在被扫描到的NFC标签上没有NDEF数据时,也会使用ACTION_TECH_DISCOVERED类型的Intent。有了这种回退机制,如果调度系统不能够帮你解析数据,那么你就可以直接使用NFC标签上数据来工作。基本步骤如下:
1. 给你希望处理的NFC标签指定ACTION_TECH_DISCOVERED类型的Intent过滤器。更多信息请看“NFC的Intent过滤”。通常,在NDEF消息不能被映射到MIME类型或URI时,或者被扫描到的NFC标签不包含NDEF数据时,NFC标签调度系统会尝试启动一个ACTION_TECH_DISCOVERED类型的Intent。更多信息,请看“NFC标签调度系统”。
2. 应用程序接收到Intent对象时,从该Intent对象中获取Tag对象:
Tag tagFromIntent = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
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