10.3 近场通信

近场通信(Near Field Communication, NFC),是一种超近距离的无线通信技术,被广泛地用于近距离、非接触式的识别和互联场景下,比如:通过NFC可将移动设备变成电子钱包,通过NFC来识别商品信息,等等。

NFC有三种工作模式。一种模式是作为信息的载体,被其他读卡设备用来识别和获取信息,在这种模式下,移动设备可以作为电子会员卡、银行卡等,在读卡设备上进行刷卡操作;另一种模式是作为信息识别工具,近距离扫描标识(Tag),识别标识中的信息,比如,扫描带有地址信息的NFC标识,快速了解商场或餐馆的信息;最后一种模式则是在设备间建立点对点(Peer-to-Peer)的连接,进行近距离地数据传输,它的传输速率与蓝牙不相上下,大约在106 Kbit/s至848 Kbit/s,但由于传输距离近不需要进行前续的配对过程,使得连接更为快速,只是能覆盖的有效距离很短(小于10厘米)。

10.3.1 基于NFC的识别和通信

在三种模式中,将Android设备用作NFC标识的扫描和读取者是最常见的工作模式,Android从2.3版本的SDK(api level 9)开始支持基于NFC通信。如图10-2所示,基于NFC的识别和通信可分为三个步骤。

首先,Android通过设备上NFC的相关硬件和驱动,发现周边的NFC设备或标识,并读取出其中包含的消息和数据。

然后,Android会发出请求事件,调用适合的界面组件来处理该NFC消息。和NFC相关的Intent-Filter共有三种。

1)android. nfc.action.NDEF_DISCOVERED。在该广播事件中,界面组件可以指明能够处理的NFC消息数据类型(NFC Data Exchange Format, NDEF)是一个Uri还是一段文本,亦或是一份多媒体数据,等等。示例如下:


//在配置文件中为组件注册NDEF_DISCOVERED事件,处理纯文本信息

<activity name="com.sample.Read_NFC_Text"…>

<intent-filter>

<action android:name=

"android.nfc.action.NDEF_DISCOVERED"/>

<data android:mimeType="text/plain"/>

</intent-filter>

</activity>


2)android. nfc.action.TECH_DISCOVERED。在该Intent-Filter中,界面组件可以声明处理特定技术标准下的NFC消息(NFC TECH),比如:基于NfcA(ISO 14443-3A)技术的消息、基于NfcB(ISO 14443-3B)技术的消息、基于NfcF(JIS 6319-4)技术的消息,等等[1]。在配置文件中,可以通过meta-data元素来声明组件支持的NFC技术:


//在配置文件中为组件注册TECH_DISCOVERED事件,处理相关技术的消息

<activity name="com.sample.TECH_NFCA_DISCOVERED"…>

<intent-filter>

<action android:name=

"android.nfc.action.TECH_DISCOVERED"/>

<meta-data android:name=

"android.nfc.action.TECH_DISCOVERED"

android:resource="@xml/nfc_tech_filter.xml"/>

</intent-filter>

</activity>

//资源文件nfc_tech_filter.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-list>

</resources>


3)android. nfc.action.TAG_DISCOVERED。在该Intent-Filter中,可以处理其他未知的NFC消息。

在三种请求事件中,NDEF_DISCOVERED的优先级最高,TAG_DISCOVERED的优先级最低。在事件传播过程中,一旦有界面组件响应高优先级的请求,其余的请求事件将不再发出。由于基于NFC的识别和通信要求设备间的距离非常近,这就需要组件能够快速地响应请求。因此,Android会直接为NFC相关的事件直接选择一个适合的界面组件,而跳过用户选择最合适组件的环节。

因此,在实际开发中,开发者需要尽可能精确地配置Intent-Filter,避免捕获无法处理的NFC事件影响用户体验;或者采取热插拔Intent-Filter的策略,将用户选择处理组件的流程放在识别之前,从而实现精准选择NFC处理组件。相关示例如下:


//构造待触发的Intent

PendingIntent pendingIntent=PendingIntent.getActivity(

this,

0,

new Intent(this, getClass()).addFlags(

Intent.FLAG_ACTIVITY_SINGLE_TOP),

0);

//构造Intent-Filter来处理NFC消息

IntentFilter ndef=new IntentFilter(

NfcAdapter.ACTION_NDEF_DISCOVERED);

ndef.addDataType("text/plain");

intentFiltersArray=new IntentFilter[]{ndef};

techListsArray=new String[][]{

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

//当界面组件位于前台时,监听相关NFC事件

public void onResume(){

super.onResume();

NfcAdapter.getDefaultAdapter(this).enableForegroundDispatch(this,

pendingIntent, intentFiltersArray, techListsArray);

}

//当界面组件位于后台状态时,停止监听

public void onPause(){

super.onPause();

NfcAdapter.getDefaultAdapter(this)

.disableForegroundDispatch(this);

}


10.3 近场通信 - 图1

图 10-2 Android的NFC支持框架

最后,在寻找到合适的界面组件后,Android会构造并调用该界面组件,由它与NFC标识进行通信。组件与NFC标识交换的数据是通过android.nfc.NdefMessage对象来封装的。每一个NdefMessage中,包含了1个或多个android.nfc.NdefRecord对象。NdefRecord是表示NDEF格式数据的基本单元,它包含类型信息、唯一的标识信息以及需要传输的数据。界面组件可以从NFC标识中读取或写入NdefMessage对象:


//从传入的Intent消息中读取数据

NdefMessage[]msgs=null;

Parcelable[]rawMsgs=intent.getParcelableArrayExtra(

NfcAdapter.EXTRA_NDEF_MESSAGES);

if(rawMsgs!=null){//将数据流转换成Ndef消息

msgs=new NdefMessage[rawMsgs.length];

for(int i=0;i<rawMsgs.length;i++){

msgs[i]=(NdefMessage)rawMsgs[i];

}

}else{//如果是无法识别的数据流,将其整体作为一条Ndef消息

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};

}

//将信息写入到NFC标识中

byte[]msgData=text.getBytes(Charsets.UTF_8);

NdefRecord record=NdefRecord(NdefRecord.TNF_WELL_KNOWN,

NdefRecord.RTD_TEXT, new byte[0],msgData);

try{

//将需要传输的数据封装成消息

NdefMessage message=new NdefMessage(

new NdefRecode{record});

//连接标识并传输

NdefFormatable tag=NdefFormatable.get(t);

tag.connect();

tag.format(message);

}catch(Exception e){

//处理错误

}


[1]Android支持的NFC协议列表,请参见:http://developer.android.com/reference/android/nfc/tech/TagTechnology.html。