Android中NFC知识介绍
近场通信(NFC)是一种短距离无线技术,通常要求距离为4cm或更低以启动连接。NFC允许你在NFC标签和android驱动的设备之间,或者在两个android驱动的设备之间共享少量的数据。
简单的标签只提供读和写的功能,有时使用一个可编程的区域使卡片只读。更复杂的标签提供数学运算,并有加密硬件来认证对一个扇区的访问。最复杂的标签包含操作环境,允许通过代码与在标签进行复杂的交互。存储在标签中的数据也可以用多种格式编写,但是Android框架api大多都是基于NDEF标准(NFC数据交换格式)。
根据Google官方文档的介绍,具有NFC的安卓设备同时支持三种主要的操作方式:
- Reader/writer mode, allowing the NFC device to read and/or write passive NFC tags and stickers.
- P2P mode, allowing the NFC device to exchange data with other NFC peers; this operation mode is used by Android Beam.
- Card emulation mode, allowing the NFC device itself to act as an NFC card. The emulated NFC card can then be accessed by an external NFC reader, such as an NFC point-of-sale terminal.
Android NFC 开发中的几个类:
NfcManager:用来管理Android设备中所有NFC Adapter,通过getSystemService(Class)来获取它的实例。
NfcAdapter:手机的NFC硬件设备,代表了设备的NFC适配器,是NFC操作的入口,大部分Android设备只支持一个NFC Adapter,可以通过getDefaultAdapter()、getDefaultAdapter(android.content.Context)来获取它的实例。
NdefMessage:表示一个NDEF数据的,这是在设备和标签之间传输数据的“records”的标准格式。程序可以通过action“ACTION_TAG_DISCOVERED”来接收这些数据。
NdefRecord: 表示一个record,该record在NdefMessage中传递,并描述共享数据的类型和数据本身。简单说一下NDEF格式:一个NDEF格式的标签使用一个NdefMessage来包装,一个NdefMessage由0个或多个NdefRecord组成,其中有各种各样的NdefRecord,比如放Url的,放文本信息的等等,这里介绍的是放文本信息的NdefRecord的格式:
一个NdefRecord由四个字段构成:
3-bit TNF:类型名称格式,可变长度类型RTD:记录类型定义,可变长度ID:唯一标识该记录,可变长度的负载:就是文本信息
Tag:代表一个被动式NFC对象,比如一张公交卡,Tag可以理解成能被手机NFC读写的对象。当Android检测到一个Tag时,会创建一个Tag对象,将其放在Intent对象中,然后发送到相应的Activity。
详见:https://developer.android.com/guide/topics/connectivity/nfc/nfc.html#p2pAndroid 支持的标签技术
当使用NFC标签和android驱动的设备时,用来读取和写入标签数据的主要格式是NDEF。当设备扫描带有NDEF数据的标签时,Android提供了解析消息的支持,并在可能的情况下将其发送到NDEF消息中。但是,当您扫描一个不包含NDEF数据或NDEF数据不能映射到MIME类型或URI的标记时,会出现一些情况。在这些情况下,需要直接与标签进行通信,并使用自己的协议(在原始字节中)读取和写入它。Android为这些用例提供了通用的支持
表 1. 支持的Tag技术类 | 描述 |
---|---|
TagTechnology | 这个接口是下面所有tag technology类必须实现的。 |
NfcA | 提供访问 NFC-A (ISO 14443-3A) 的属性和 I/O 操作 |
NfcB | 提供访问 NFC-B (ISO 14443-3B) 的属性和 I/O 操作 |
NfcF | 提供访问 NFC-F (JIS 6319-4) 的属性和 I/O 操作 |
NfcV | 提供访问 NFC-V (ISO 15693) 的属性和 I/O 操作 |
IsoDep | 提供访问 ISO-DEP (ISO 14443-4) 的属性和 I/O 操作 |
Ndef | 提供对那些被格式化为NDEF的tag的数据的访问和其他操作 |
NdefFormatable | 对那些可以被格式化成NDEF格式的tag提供一个格式化的操作 |
表 2. 可选的支持的Tag技术
类 | 描述 |
---|---|
MifareClassic | 如果android设备支持MIFARE,提供对MIFARE Classic目标的属性和I/O操作。 |
MifareUltralight | 如果android设备支持MIFARE,提供对MIFARE Ultralight目标的属性和I/O操作。 |
https://developer.android.com/guide/topics/connectivity/nfc/advanced-nfc.htm
Android NFC调度系统
NFC调度是指手机检测到NFC对象后如何处理,调度系统分为前台调度系统(Foreground Dispatch System)和标签调度系统(NFC Tag Dispatch System)
1) 前台调度系统NFC前台调度系统是一种用于在运行的程序中(前台呈现的Activity)处理Tag的技术,即前台调度系统允许Activity拦截Intent对象,并且声明该Activity的优先级比其他的处理Intent对象的Activity高。前台调度系统在一些涉及需要在前台呈现的页面中直接获取或推送NFC信息时十分方便。本文的示例就是使用前台调度。
2) NFC标签调度系统当标签分派系统完成创建一个封装NFC标签及其标签信息的意图时,它将意图发送到一个可以处理该意图的应用程序。当存在多个可以处理该Tag信息的application时,系统会弹出一个Activity Chooser,供用户选择哪个application来处理这个意图。标签调度系统定义了3种意图,按照优先级由高到低分别为:
- ACTION_NDEF_DISCOVERED: This intent is used to start an Activity when a tag that contains an NDEF payload is scanned and is of a recognized type. This is the highest priority intent, and the tag dispatch system tries to start an Activity with this intent before any other intent, whenever possible.
- ACTION_TECH_DISCOVERED: If no activities register to handle the ACTION_NDEF_DISCOVERED intent, the tag dispatch system tries to start an application with this intent. This intent is also directly started (without starting ACTION_NDEF_DISCOVERED first) if the tag that is scanned contains NDEF data that cannot be mapped to a MIME type or URI, or if the tag does not contain NDEF data but is of a known tag technology.
- ACTION_TAG_DISCOVERED: This intent is started if no activities handle the ACTION_NDEF_DISCOVERED or ACTION_TECH_DISCOVERED intents.
详见:https://developer.android.com/guide/topics/connectivity/nfc/nfc.html#tag-dispatch
Android NFC编程示例(使用前台调度系统)
package com.bugull.nfctest;
import android.app.PendingIntent;
import android.content.Intent;
import android.content.IntentFilter;
import android.nfc.NfcAdapter;
import android.nfc.Tag;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.widget.TextView;
import android.widget.Toast;
/**
* @author kk
*/
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
private NfcAdapter mNfcAdapter;
private TextView mTextView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mTextView = findViewById(R.id.tv_nfc_detail);
mNfcAdapter = NfcAdapter.getDefaultAdapter(getApplicationContext());
if (mNfcAdapter == null) {
Toast.makeText(this, "该设备不支持nfc", Toast.LENGTH_SHORT).show();
finish();
return;
}
if (!mNfcAdapter.isEnabled()) {
startActivity(new Intent("android.settings.NFC_SETTINGS"));
Toast.makeText(this, "设备未开启nfc", Toast.LENGTH_SHORT).show();
}
}
@Override
protected void onPause() {
super.onPause();
mNfcAdapter.disableForegroundDispatch(this);
}
@Override
protected void onResume() {
super.onResume();
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, new Intent(this,
getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);
IntentFilter[] intentFilters = new IntentFilter[]{};
mNfcAdapter.enableForegroundDispatch(this, pendingIntent, intentFilters, null);
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(intent.getAction())
|| NfcAdapter.ACTION_TECH_DISCOVERED.equals(intent.getAction()) || NfcAdapter.ACTION_TAG_DISCOVERED.equals(intent.getAction())) {
Tag iTag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
mTextView.setText(TagReader.readTag(iTag, intent));
}
}
}
效果:
源码下载:
https://github.com/yingka/nfc-reader
参考资料:
https://nfc-forum.org/
https://developer.android.com/guide/topics/connectivity/nfc/advanced-nfc.html#foreground-dispatch