1. 跨平台NFC开发入门指南
最近在做一个智能门禁项目时,需要实现手机读写NFC卡的功能。经过反复测试,我发现市面上常见的MifareUltralight卡(芯片型号215)是个不错的选择,价格便宜(1-2元/张)且兼容性好。但实际开发过程中,Android和iOS平台的差异让我踩了不少坑,特别是iOS的读写限制让人头疼。
如果你是刚接触NFC开发的工程师,可能会遇到这些问题:为什么同样的卡在安卓上能直接读写,在iPhone上却只能读不能写?为什么读取的卡号显示乱码?如何正确设置卡的Lock位?这篇文章将用最直白的语言,带你完整走通Android和iOS双平台的开发流程。
2. 开发环境准备
2.1 硬件选购要点
建议购买前先确认卡片参数:
- 芯片型号必须是MifareUltralight(常见型号215)
- 存储容量512bit(16页×4字节)
- 支持ISO14443-3A协议
我在淘宝买的卡片单价1.5元,测试了20张全部可用。有个小技巧:让卖家提供NDEF格式测试记录,确保卡片出厂时未被错误锁定。
2.2 安卓开发环境
Android Studio配置很简单:
- 新建项目时最低API设为19(Android 4.4)
- 在AndroidManifest.xml添加权限:
<uses-permission android:name="android.permission.NFC" /> <uses-feature android:name="android.hardware.nfc" android:required="true" />- 在res/xml下新建nfc_filter.xml:
<tech-list> <tech>android.nfc.tech.MifareUltralight</tech> </tech-list>2.3 iOS开发环境配置
Xcode需要额外注意:
- 必须使用iPhone 7及以上机型
- iOS版本需≥13.0(建议用最新稳定版)
- 在工程配置中启用Near Field Communication Tag Reading
- 添加NFC权限描述:
<key>NFCReaderUsageDescription</key> <string>需要NFC权限读取门禁卡</string>3. Android端核心开发
3.1 基础读写实现
先初始化NFC适配器:
NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(this); PendingIntent pendingIntent = PendingIntent.getActivity( this, 0, new Intent(this, getClass()) .addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);重写onNewIntent处理卡片感应:
@Override protected void onNewIntent(Intent intent) { super.onNewIntent(intent); Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG); if (tag != null) { readCard(tag); // 自定义读取方法 } }3.2 关键代码解析
读取序列号的正确姿势:
public String readSerialNumber(Tag tag) throws Exception { MifareUltralight mu = MifareUltralight.get(tag); try { mu.connect(); byte[] page0 = mu.readPages(0); byte[] page1 = mu.readPages(1); // 组合SN0-SN6共7字节 byte[] sn = new byte[7]; System.arraycopy(page0, 0, sn, 0, 3); System.arraycopy(page1, 0, sn, 3, 4); return bytesToHex(sn); // 转16进制字符串 } finally { mu.close(); } }写入Lock位的注意事项:
public void lockPages(Tag tag, int startPage) throws Exception { MifareUltralight mu = MifareUltralight.get(tag); try { mu.connect(); // 计算lock位(示例锁定4-7页) byte lock0 = (byte) 0b00001111; // 前4页不锁 byte lock1 = (byte) 0b11110000; // 后4页锁定 mu.writePage(2, new byte[]{0, 0, lock0, lock1}); } finally { mu.close(); } }4. iOS端特殊处理
4.1 读取限制突破方案
由于iOS的封闭性,实测发现:
- 无法直接读取原始序列号
- 必须先用安卓写入NDEF消息
- iPhone只能读取NDEF格式内容
解决方案分三步:
- 在Android设备上用NXP TagWriter写入测试数据
- 格式选择"Text"类型
- 内容建议包含卡号(如"Card:XXXXXX")
4.2 Swift实现代码
初始化阅读会话:
import CoreNFC class NFCReader: NSObject, NFCNDEFReaderSessionDelegate { func beginScan() { guard NFCNDEFReaderSession.readingAvailable else { print("设备不支持NFC") return } let session = NFCNDEFReaderSession( delegate: self, queue: nil, invalidateAfterFirstRead: true) session.begin() } func readerSession(_ session: NFCNDEFReaderSession, didDetectNDEFs messages: [NFCNDEFMessage]) { for message in messages { for record in message.records { if let payload = String(data: record.payload, encoding: .utf8) { print("读取到数据: \(payload)") } } } } }5. 避坑指南
5.1 常见问题排查
- 读取返回空数据?
- 检查卡片是否已锁定
- 确认使用了正确的readPages参数
- 写入失败?
- Page2-3是特殊控制页,不要随意写入
- 确保没有重复锁定已锁定的页
- iOS无法识别?
- 必须先用安卓写入NDEF格式数据
- 检查iPhone的NFC功能是否开启
5.2 安全注意事项
- Lock位一旦设置无法撤销
- Page3是OTP区,写入后永久不可改
- 建议先读取全部数据备份后再操作
实际项目中,我遇到过团队小伙伴误锁整张卡的情况。后来我们建立了标准操作流程:开发阶段使用未锁定卡片,正式环境才启用写保护。