Android端NFC写入失败导致空标签问题求助
NFC标签频繁操作导致URI被擦除的问题修复
核心问题分析
- 写入操作无原子性保护:NFC标签写入分块执行,中途移卡会导致部分写入,破坏原有NDEF结构,使标签失效。
- 重复触发写入:短时间多次刷卡会重复调用
writeNfcDataToDevice,多个写入操作冲突,损坏标签。 - NfcData构造函数缺陷:存在语法错误,且未处理读取不到URI的场景,可能导致空指针或后续逻辑异常。
- 写入前校验不足:未检查标签连接状态、可用空间,写入后也未验证结果。
具体修复方案
1. 增加操作防抖与锁机制
避免短时间内重复触发写入操作,在MainActivity中添加锁和时间判断:
private boolean isWriting = false; private long lastWriteTime = 0; private static final long WRITE_COOLDOWN = 2000; // 2秒冷却时间 private void writeNfcDataToDevice(Intent intent, String myString) { // 防抖判断 long currentTime = System.currentTimeMillis(); if (isWriting || (currentTime - lastWriteTime) < WRITE_COOLDOWN) { Log.d(LOG_TAG, "操作过于频繁,跳过本次写入"); return; } isWriting = true; lastWriteTime = currentTime; try { // 原有写入逻辑... } catch (Exception exception) { // 异常处理... } finally { isWriting = false; } }
2. 增强写入的原子性与容错处理
优化写入流程,增加校验和异常处理:
private void writeNfcDataToDevice(Intent intent, String myString) { // 防抖判断... try { Tag nfcTag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG); if (nfcTag == null) { Log.e(LOG_TAG, "未获取到NFC标签"); return; } NfcData nfcData = new NfcData(intent); Uri existingUri = nfcData.getUri(); if (existingUri == null || !URI_PATTERN.matcher(existingUri.toString()).matches()) { Log.d(LOG_TAG, "URI格式不匹配或为空:" + existingUri); return; } // 构建新URI Uri newUri = existingUri.buildUpon().clearQuery().appendQueryParameter(NfcData.MY_PARAM, myString).build(); NdefMessage uriNdefMessage = createUriNdefMessage(newUri); Ndef ndef = Ndef.get(nfcTag); if (ndef == null) { Log.e(LOG_TAG, "标签不支持NDEF格式"); return; } // 检查标签是否连接、是否可写 if (!ndef.isConnected()) { ndef.connect(); } if (!ndef.isWritable()) { Log.e(LOG_TAG, "标签不可写"); ndef.close(); return; } // 检查可用空间 if (ndef.getMaxSize() < uriNdefMessage.toByteArray().length) { Log.e(LOG_TAG, "标签空间不足"); ndef.close(); return; } // 执行写入 ndef.writeNdefMessage(uriNdefMessage); // 写入后立即校验 NdefMessage writtenMsg = ndef.getNdefMessage(); if (Arrays.equals(uriNdefMessage.toByteArray(), writtenMsg.toByteArray())) { Log.d(LOG_TAG, "NFC写入成功且校验通过"); } else { Log.e(LOG_TAG, "写入内容与预期不符,标签可能损坏"); } ndef.close(); } catch (IOException e) { Log.e(LOG_TAG, "写入中断或标签断开:" + e.toString()); // 若写入中途断开,可尝试恢复原有数据(可选,需提前备份) } catch (Exception exception) { Log.e(LOG_TAG, "写入失败:" + exception.toString()); } finally { isWriting = false; } }
3. 修复NfcData构造函数的语法与空指针问题
public NfcData(Intent intent) { Uri uri = null; Parcelable[] rawMsgs = intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES); if (rawMsgs != null) { for (Parcelable parcelable : rawMsgs) { if (!(parcelable instanceof NdefMessage)) continue; NdefMessage msg = (NdefMessage) parcelable; for (NdefRecord record : msg.getRecords()) { try { uri = record.toUri(); } catch (IllegalArgumentException e) { // 处理无效URI记录 continue; } if (uri != null) { break; } } if (uri != null) { break; } } } this.mUri = uri; // 确保无论如何都初始化mUri }
4. 优化ForegroundDispatch的IntentFilter
使用更精准的ACTION_NDEF_DISCOVERED,减少不必要的触发:
@Override protected void onResume() { super.onResume(); if (nfcAdapter != null) { PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, new Intent(this, this.getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), PendingIntent.FLAG_MUTABLE); // 针对目标URI的过滤,只处理符合格式的NDEF标签 IntentFilter ndefDiscovered = new IntentFilter(NfcAdapter.ACTION_NDEF_DISCOVERED); try { ndefDiscovered.addDataType("text/uri-list"); } catch (IntentFilter.MalformedMimeTypeException e) { throw new RuntimeException("Invalid MIME type", e); } ndefDiscovered.addCategory(Intent.CATEGORY_DEFAULT); IntentFilter[] writeTagFilters = new IntentFilter[]{ndefDiscovered}; nfcAdapter.enableForegroundDispatch(this, pendingIntent, writeTagFilters, null); } }
额外建议
- 写入过程中给用户显示加载提示,避免用户过早移走标签
- 对于关键标签,可提前备份原有NDEF数据,写入失败时尝试恢复
- 测试时模拟标签断开场景,验证容错逻辑是否生效
内容的提问来源于stack exchange,提问作者Umang Mathur
相关产品推荐
相关产品推荐

