Android无法读取NFC 4K卡数据求助 附完整配置及实现代码
NFC 4K卡读取失败修复方案
现有代码存在的问题
- 仅实现了标签发现的回调逻辑,未针对Mifare Classic 4K卡编写认证、读块的核心读卡逻辑
enableReaderMode方法传入的flags参数使用Integer.MAX_VALUE不符合规范,无法正确匹配读卡技术- AndroidManifest中intent-filter配置了
text/plain的mimeType限制,会过滤非NDEF文本格式的卡,影响非标准NDEF的4K卡识别
具体修复步骤
1. 修正AndroidManifest配置
删除intent-filter标签下的<data android:mimeType="text/plain" />配置,避免拦截非文本NDEF格式的NFC卡。
修改后对应配置如下:
<intent-filter> <action android:name="android.nfc.action.NDEF_DISCOVERED" /> <action android:name="android.nfc.action.TECH_DISCOVERED" /> <action android:name="android.nfc.action.TAG_DISCOVERED" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> <meta-data android:name="android.nfc.action.TECH_DISCOVERED" android:resource="@xml/nfc_tech_filter" />
2. 修正ReaderMode的flags参数
将enableReaderMode方法中的Integer.MAX_VALUE替换为明确的NFC技术标志位,同时添加跳过系统NDEF检查的标志,提升读卡效率:
private void enableReaderMode() { showToast("Enabling reader mode..."); Activity activity = MainActivity.this; nfcAdapter = NfcAdapter.getDefaultAdapter(activity); if (nfcAdapter != null) { int flags = NfcAdapter.FLAG_READER_NFC_A | NfcAdapter.FLAG_READER_NFC_B | NfcAdapter.FLAG_READER_NFC_F | NfcAdapter.FLAG_READER_NFC_V | NfcAdapter.FLAG_READER_SKIP_NDEF_CHECK; nfcAdapter.enableReaderMode(activity, this, flags, null); } }
3. 补充Mifare Classic 4K卡的读卡逻辑
Mifare Classic 4K卡属于加密卡,读卡前需要先验证对应扇区的密钥,默认通用密钥为FF FF FF FF FF FF,如果是定制卡需要替换为对应密钥。在onTagDiscovered方法中添加如下逻辑:
@Override public void onTagDiscovered(Tag tag) { Log.d(TAG, "New tag discovered..."); MifareClassic mifareClassic = MifareClassic.get(tag); if (mifareClassic == null) { Log.d(TAG, "非Mifare Classic卡,不支持读取"); return; } try { mifareClassic.connect(); // 验证卡类型 int cardSize = mifareClassic.getSize(); if (cardSize != MifareClassic.SIZE_4K) { Log.d(TAG, "当前卡不是4K容量,实际容量:" + cardSize + "字节"); mifareClassic.close(); return; } int sectorCount = mifareClassic.getSectorCount(); Log.d(TAG, "卡总扇区数:" + sectorCount); // 默认密钥,可替换为实际卡的密钥 byte[] keyA = {(byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF}; for (int sector = 0; sector < sectorCount; sector++) { // 认证当前扇区密钥A,也可使用authenticateSectorWithKeyB验证密钥B boolean authSuccess = mifareClassic.authenticateSectorWithKeyA(sector, keyA); if (!authSuccess) { Log.d(TAG, "扇区" + sector + "密钥认证失败,跳过读取"); continue; } int blockCount = mifareClassic.getBlockCountInSector(sector); int firstBlockIndex = mifareClassic.sectorToBlock(sector); // 遍历读取当前扇区所有数据块,跳过最后一块的密钥区 for (int i = 0; i < blockCount - 1; i++) { byte[] blockData = mifareClassic.readBlock(firstBlockIndex + i); Log.d(TAG, "扇区" + sector + " 块" + (firstBlockIndex + i) + " 数据:" + bytesToHex(blockData)); } } mifareClassic.close(); } catch (IOException e) { Log.e(TAG, "读卡异常:" + e.getMessage(), e); try { if (mifareClassic.isConnected()) { mifareClassic.close(); } } catch (IOException ex) { ex.printStackTrace(); } } } // 字节数组转16进制字符串工具方法 private String bytesToHex(byte[] src) { StringBuilder stringBuilder = new StringBuilder(); if (src == null || src.length <= 0) { return null; } for (byte b : src) { int v = b & 0xFF; String hv = Integer.toHexString(v); if (hv.length() < 2) { stringBuilder.append(0); } stringBuilder.append(hv).append(" "); } return stringBuilder.toString().trim(); }
注意事项
- 读卡操作默认在子线程执行,若需将读取结果展示到UI,需主动切到主线程操作
- 若默认密钥认证失败,说明卡为加密卡,需获取对应卡的正确密钥才能正常读取
- 部分搭载国产NFC芯片的安卓设备对Mifare Classic卡的兼容性较差,读卡失败可更换设备尝试
内容的提问来源于stack exchange,提问作者SHIDHIN TS
相关产品推荐
相关产品推荐

