近场通信(NFC)是一种短距离无线技术,通常要求距离为4cm或更低以启动连接。NFC允许你在NFC标签和android驱动的设备之间,或者在两个android驱动的设备之间共享少量的数据。
简单的标签只提供读和写的功能,有时使用一个可编程的区域使卡片只读。更复杂的标签提供数学运算,并有加密硬件来认证对一个扇区的访问。最复杂的标签包含操作环境,允许通过代码与在标签进行复杂的交互。存储在标签中的数据也可以用多种格式编写,但是Android框架api大多都是基于NDEF标准(NFC数据交换格式)。
根据Google官方文档的介绍,具有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#p2p当使用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操作。 |
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种意图,按照优先级由高到低分别为:
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