You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android端NFC写入失败导致空标签问题求助

NFC标签频繁操作导致URI被擦除的问题修复

核心问题分析

  1. 写入操作无原子性保护:NFC标签写入分块执行,中途移卡会导致部分写入,破坏原有NDEF结构,使标签失效。
  2. 重复触发写入:短时间多次刷卡会重复调用writeNfcDataToDevice,多个写入操作冲突,损坏标签。
  3. NfcData构造函数缺陷:存在语法错误,且未处理读取不到URI的场景,可能导致空指针或后续逻辑异常。
  4. 写入前校验不足:未检查标签连接状态、可用空间,写入后也未验证结果。

具体修复方案

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 02:37:04