对NFC(BUG)的初步认识
多年未曾更新的帖子,今天再记录一下,上家公司用到的新技术 NFC点对点通信 。一个让人“疯狂”的技术。
概念
根据官方解释,其是近距离无线通信 (NFC) 是一组近距离无线技术,通常只有在距离不超过 4 厘米时才能启动连接。借助 NFC,可以在 NFC 标签与 Android 设备之间或者两台 Android 设备之间共享小型负载。
支持 NFC 的 Android 设备同时支持以下三种主要操作模式:
- 读取器/写入器模式 ,支持 NFC 设备读取和/或写入被动 NFC 标签和贴纸;
- 点对点模式 ,支持 NFC 设备与其他 NFC 对等设备交换数据;Android Beam 使用的就是此操作模式;
- 卡模拟模式 ,支持 NFC 设备本身充当 NFC 卡。然后,可以通过外部 NFC 读取器(例如 NFC 销售终端)访问模拟 NFC 卡。
本文章主要讲的是 点对点模式,根据实际项目的使用,对此做过简单记录。
首先还是常规操作,获取权限:
<uses-permission android:name="android.permission.NFC" />
<uses-feature android:name="android.hardware.nfc" android:required="true" />
接下来就是对NFC的具体操作,需要跟你的兄弟部门去沟通,这个NFC支持哪些标签,以及项目中需要使用什么标签来进行通信。具体的标签内容,可以查看 官方文档,我项目中使用的是IsoDep。
初始化NFC
NFC的初始化就像蓝牙一样,获取其的NfcAdapter即可:
mNfcAdapter = NfcAdapter.getDefaultAdapter(context);
接着,我们得判断你的设备是否支持,且是否打开了NFC功能:
public NFCState ifNFCUse() {
if (this.mNfcAdapter == null) {
return NFCState.STATE_UNSUPPORTED;
} else {
return !this.mNfcAdapter.isEnabled() ? NFCState.STATE_CLOSE : NFCState.STATE_OPEN;
}
}
获取NFC标签
获取通信标签有两种方式:
1.通过在Activity页面实现onNewIntent()方法;
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
...
if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(intent.getAction())) {
Parcelable[] rawMessages =
intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
...
}
}
2.通过BroadcastReceiver实现;
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
if (tag != null && tag.getTechList() != null) {
Log.d("find_nfc_iso", "nfc iso : " + Arrays.toString(tag.getTechList()));
//这里我做了一次重置,因为NFC有个特性,每次进行连接,必须断开上一次的连接操作,才能继续连接
DeviceService.resetNfc();
//这里做个例子通过遍历标签集合,来判断获取是否有你需要的标签
for (String tech :
tag.getTechList()) {
if (tech.equals("android.nfc.tech.IsoDep")) {
NfcStateReceiver.tag = tag;
NfcStateReceiver.isoDep = IsoDep.get(tag);
break;
}
}
}
if (null == action || action.isEmpty()) {
return;
}
//定义了一个接口,根据获取到的action类型,来及时的更新连接状态
if (action.equals(NfcAdapter.ACTION_ADAPTER_STATE_CHANGED)) {
int state = intent.getIntExtra("android.nfc.extra.ADAPTER_STATE", DEFAULT_VALUE_NFC);
switch (state) {
case 1:
this.onNfcStateListener.onStateOff();
DeviceService.resetNfc();
break;
case 2:
this.onNfcStateListener.onStateTurningOn();
break;
case 3:
this.onNfcStateListener.onStateOn();
break;
case 4:
this.onNfcStateListener.onStateTurningOff();
}
}
}
连接
通过上面我们已经获取到了nfc,以及通信标签,接下来就是连接的操作,如下所示:
public Print
Nfc(IsoDep iso, Tag nfcTag) {
this.iso = iso;
try {
if (this.iso == null) {
Log.d(TAG, "not find iso,nfc connected failed");
return;
}
//因NFC特殊性,连接前判断是否已连接
if (this.iso.isConnected()) {
//释放连接状态
this.iso.close();
}
//连接
this.iso.connect();
} catch (IOException e) {
e.printStackTrace();
}
this.nfcTag = nfcTag;
//这里是个心跳,为了保证跟另外一台设备通信正常,我设置的是500毫秒发送一次
createDownTimer();
}
发送数据
NFC的数据发送跟蓝牙以及WIFI的发送方式完全不同,BLE蓝牙通过gatt发送,BT蓝牙或者WIFI可以通过socket发送,而NFC是通过标签进行发送的,拿我们获取的IsoDep,调用 transceive(byte[] data) 方法来进行发送,如下:
iso.transceive(ICommand.HEART_BEAT);
注意 :如果你的数据长度在24个字节以内,你是可以直接发送的。如果超出24个byte(这个长度数我不确定是不是每个厂家都不一样),那就得分段发送。
接收数据
发送完成了,接下来就是我们的接收数据的环节,如下:
receipt = iso.transceive(ICommand.HEART_BEAT);
是的,你没看错,NFC的接收也是通过transceive(byte[] data) 方法来进行的,他有一个byte[]的回参。能拿到这个回参,恭喜你通信成功。
最紧要的—BUG
1.在你做iso.connect() 的操作时,一定要确保当前是未连接的状态,否则你会报错,提示你 “无法连接,让你先释放上一个” 。
2.在你发送数据的时候,即调用 iso.transceive() 的时候,如果你是第一次使用NFC功能,你会发现一个问题,他会给你一个异常 “tag was lost” 。不管你检如何查自己的代码,你都发现没有问题,这就是我说的NFC通信特殊的原因。具体原因你可以理解为,假设另外一放未单片机,单片机需要一直给NFC的中控发送信息,如果单片机发送慢了,那就会导致这个异常产生。是的,不光你的APP跟单片机有个心跳机制,单片机跟NFC模块之间也有一个类似心跳机制的东西存在。
OVER