Android系统编程入门系列之硬件交互——通信硬件NFC
在上篇文章介绍了接入式USB硬件的简单使用,接下来将介绍不依赖物理连接的硬件通信了。本文的重点是近距离通信的硬件NFC。
NFC硬件
应用程序中可以通过NFC硬件读取或发送指定协议的技术实现,在Android10.0之前甚至可以封装大段的NDEF数据。另外,Android系统基于NFC的特性,结合移动设备的安全元件,构建了一层HCE架构,从而应用于安全级别较高的公交卡刷卡或付款交易等操作。
权限声明
要想使用NFC硬件的相关功能,必须要声明权限,在应用程序的清单文件中声明<uses-permission />
标签,其中的android:name
属性值为android.permission.NFC
。
当然,为了强调应用程序需要运行在支持NFC硬件的设备上,也可以在应用程序的清单文件中声明<uses-feature />
标签,其中的android:name
属性值为android.hardware.nfc
。
使用流程
由于NFC硬件是在近距离接触后即可触发,所以首先要在应用程序中确定触发NFC连接后要响应的界面Activity
。在清单文件注册的<activity></activity>
标签内部,增加意图过滤<intent-filter></intent-filter>
标签,并在该标签内部添加<action />
标签,并指定android:name
属性值分别为android.nfc.action.NDEF_DISCOVERED
作为NDEF数据传输行为、android.nfc.action.TECH_DISCOVERED
作为技术标签行为或android.nfc.action.TAG_DISCOVERED
作为普通行为。
上边指定的意图行为有三种方式,但是在连接NFC硬件后,系统将按照上述顺序优先启动对应的界面Activity
。另外针对上述三种不同的意图行为,还要在清单文件下分别追加相应的配置。
在通过上述三种方式收到Intent
意图启动的界面Activity
中,可以通过getIntent()
获取启动传入的Intent
意图对象。在该对象中,可以接收其他NFC硬件输入的标签及相关内容。
默认功能
针对action
值为android.nfc.action.TAG_DISCOVERED
的配置,由于是优先级最低的意图行为,因此在上面两种意图行为及配置未调起时,就会调用该意图行为绑定的界面Activity
。因此该意图行为下不需要其他的配置参数。
在启动的界面Activity
中,调用Intent
对象的getParcelableExtra(String name)
方法,传入参数 name 值为NfcAdapter.EXTRA_TAG="android.nfc.extra.TAG"
,得到android.nfc.TagNFC标签类型的对象,该对象中记录了标签的基本信息。
NDEF数据传输功能
针对action
值为android.nfc.action.NDEF_DISCOVERED
的配置,还要声明要传输的数据类型。同样在意图过滤的<intent-filter></intent-filter>
标签内部,添加<data />
标签。可支持传输的数据类型可以使用的表示方式,包括 MIME TYPE 类型的结构,和 URI 结构,二选一皆可。在<data />
标签中使用android:mimeType
属性,可以设置 MIME TYPE 结构的数据类型,通常如属性值为text/plain
的文本类型。另外,在<data />
标签中使用android:scheme
、android:host
、android:pathPrefix
三个属性同时标注URI内容。
在启动的界面Activity
中,调用Intent
对象的getParcelableArrayExtra(String name)
方法,传入参数 name 值为NfcAdapter.EXTRA_NDEF_MESSAGES="android.nfc.extra.NDEF_MESSAGES"
,得到Parcelable[]
数组可以分别强转为android.nfc.NdefMessage用以保存消息内容的类型对象数组。当使用android.nfc.action.NDEF_DISCOVERED
意图行为过滤的NFC硬件启动后,可通过该方法获取NDEF消息内容。
在应用程序内可以自定义NDEF消息体内容并发送。借助android.nfc.NdefRecordNFC记录类。在该类中有多个静态方法,包括createExternal(String domain, String type, byte[] data)
创建有携带数据的NFC记录对象;createMime(String mimeType, byte[] mimeData)
创建MIME类型的NFC记录对象;createTextRecord(String languageCode, String text)
创建短文本内容的NFC记录对象;createUri (Uri uri)
创建Uri
类型的NFC记录对象等。
在创建NdefRecord
对象之后,可以作为参数传入NdefMessage
NFC消息类的构造方法中,从而创建NdefMessage
对象,将该对象作为参数,传入NdefAdapter
对象的setNdefPushMessage(NdefMessage message, Activity activity, Activity... activities)
方法中,这样在当前参数 activity 中将会一直发送参数 message 中的内容。
从Android10.0即API级别29开始,NFC功能的NDEF消息传输功能就被废弃了,因此其相关功能方法在以后的版本中也将不再支持。
标签技术功能
针对action
值为android.nfc.action.TECH_DISCOVERED
的配置,还要通过资源文件声明要依赖的技术集。资源文件的声明是在注册的<activity></activity>
标签内部,添加<meta-data />
标签,同样设置其属性android:name
值为android.nfc.action.TECH_DISCOVERED
,更别忘了设置属性android:resource
,其值为保存在 res/xml 目录下的资源文件。而资源文件的定义,是在 res/xml 路径下,定义 xml 格式的资源文件,在该文件中使用<tech-list></tech-list>
标签作为最外层的集合,在该标签中包含了一堆使用<tech>
标签定义的可支持技术。这些技术的定义均实现了android.nfc.tech.TagTechnology标签技术接口,在各种实现类中定义了相应技术的常量值。
在启动的界面Activity
中,同样可以调用Intent
对象的getParcelableExtra(String name)
方法,传入参数 name 值为[NfcAdapter.EXTRA_TAG]得到TAG
NFC标签类的对象。
另外可以通过其getTechList()
方法,获取通过意图行为android.nfc.action.TECH_DISCOVERED
启动的NFC硬件中所使用技术列表。其中系统支持的技术类型均实现了android.nfc.tech.TagTechnology标签技术接口。
在得到的Tag
NFC标签类的对象后,可以遵循相关技术类型对标签进行读写操作。对实现了TagTechnology
接口的具体技术类中,可以调用静态方法get(Tag tag)
得到具体的技术类对象,其参数即得到的Tag
NFC标签类对象。在实现TagTechnology
接口的对象中,需要首先调用connect()
建立连接;之后可以根据不同的技术实现,调用相关的读写操作方法;最终在操作结束后,调用close()
断开连接即可。
HCE服务功能
该功能是由Android系统实现的一套在后台使用NFC进行交易等服务的架构。该架构主要依赖android.nfc.cardemulation.HostApduService抽象服务类。在自定义的服务中必须继承自HostApduService
服务类,并实现其两个抽象方法,接收并响应数据的byte[] processCommandApdu(byte[] apdu, Bundle extras)
方法,和NFC切换或关闭当前APDU连接时的void onDeactivated(int reason)
方法。
在定义了继承自HostApduService
的服务之后,需要在清单文件中注册该服务组件,在<service></service>
标签中要设置android:exported
属性值为true
。同时在标签内嵌入<intent-filter></intent-filter>
意图过滤标签中,设置行为值为HostApduService.SERVICE_INTERFACE="android.nfc.cardemulation.action.HOST_APDU_SERVICE"
。另外要在标签内嵌入<meta-data />
额外数据标签,设置其android:name
属性值为HostApduService.SERVICE_META_DATA="android.nfc.cardemulation.host_apdu_service"
,设置其android:resource
属性为包含 AID 群组的资源文件。
定义的 AID 群组,是由NFC卡提供的应用唯一标识,如用于支付的银联储蓄卡、公交卡等,每张卡都有唯一的16字节组成的AID。而在资源文件中可以定义该应用程序自定义的HostApduService
服务中允许交易的 AID 群组,该群组的所有 AID 只要有一个 AID 连接当前设备的 NFC 硬件,都会唤起 HCE 服务。
在应用程序的资源文件 res/xml 目录下,可以创建自定义的资源文件以定义上述 AID 群组,在资源文件中根标签为<host-apdu-service></host-apdu-service>
,在其中可以嵌入多组标签<aid-group></aid-group>
用来标记 AID 群组,其中设置android:description
属性值为字符串类型的群组介绍,还需要设置android:category
属性值为android.nfc.cardemulation.CardEmulation.CATEGORY_PAYMENT="payment"
作为支付使用、或者属性值为android.nfc.cardemulation.CardEmulation.CATEGORY_OTHER="other"
。在该标签内部,就可以嵌入多组<aid-filter / >
标签并设置其android:name
属性值,用以标记当前群组下的 AID 值。