当前位置: 首页 > news >正文

Android NFC 实战:从权限配置到地铁卡数据解析

1. Android NFC开发入门:从零开始配置权限

第一次接触Android NFC开发时,我完全被各种技术术语搞晕了。NFC到底是什么?简单来说,它就像手机和卡片之间的"悄悄话"通道,距离必须在4厘米内才能交流。想象一下,你用手机刷公交卡时那种"碰一下"的感觉,就是NFC在工作。

要在Android应用中启用NFC功能,首先得在AndroidManifest.xml文件中进行配置。这里有个坑我踩过:如果你忘记声明NFC权限,应用会直接崩溃。正确的配置应该是这样的:

<uses-permission android:name="android.permission.NFC" /> <uses-feature android:name="android.hardware.nfc" android:required="true" />

特别注意第二个uses-feature标签,它确保你的应用只会出现在支持NFC的设备上。我在一个项目中曾经漏掉这个配置,结果用户在不支持NFC的手机上安装后各种报错,教训深刻啊!

2. 理解NFC标签调度系统

NFC标签调度系统就像个尽职的邮递员,当手机检测到NFC标签时,它会决定哪个应用应该处理这个标签。这个系统定义了三种Intent,按优先级排序:

  1. ACTION_NDEF_DISCOVERED:最高优先级,处理包含NDEF数据的标签
  2. ACTION_TECH_DISCOVERED:中等优先级,处理已知技术类型的标签
  3. ACTION_TAG_DISCOVERED:最低优先级,作为最后的备选方案

实际开发中,我发现大多数公交卡、门禁卡都会触发ACTION_TECH_DISCOVERED。为了捕获这些Intent,需要在AndroidManifest.xml中这样配置:

<intent-filter> <action android:name="android.nfc.action.TECH_DISCOVERED" /> </intent-filter> <meta-data android:name="android.nfc.action.TECH_DISCOVERED" android:resource="@xml/nfc_tech_filter" />

3. 创建NFC技术过滤器

nfc_tech_filter.xml文件就像是NFC的"通行证",告诉系统你的应用能处理哪些类型的NFC标签。这个文件需要放在res/xml目录下。我建议新手开发者一开始就配置支持所有类型,避免漏掉某些特殊卡片:

<resources> <tech-list> <tech>android.nfc.tech.IsoDep</tech> </tech-list> <tech-list> <tech>android.nfc.tech.NfcA</tech> </tech-list> <!-- 其他技术类型... --> </resources>

记得我在处理某款门禁卡时,因为没有包含NfcF技术类型,导致应用完全检测不到卡片。后来通过日志排查才发现问题所在,所以建议新手尽量包含所有常见技术类型。

4. 实现NFC数据读取功能

现在来到最核心的部分 - 实际读取NFC卡片数据。以北京地铁卡为例,我们需要在Activity中处理NFC Intent:

private fun processIntent(intent: Intent) { val tag = intent.getParcelableExtra<Tag>(NfcAdapter.EXTRA_TAG) tag?.let { val cardId = it.id.toHexString() binding.tvContent.text = "卡片ID: $cardId" Toast.makeText(this, "读取到卡片ID: $cardId", Toast.LENGTH_LONG).show() } } fun ByteArray.toHexString() = joinToString("") { "%02x".format(it) }

这段代码做了几件事:

  1. 从Intent中获取Tag对象
  2. 提取卡片的唯一ID(字节数组)
  3. 将字节数组转换为16进制字符串显示

我在实际测试中发现,不同厂家的卡片ID格式可能不同。有些是4字节,有些是7字节,这个需要根据具体业务需求处理。

5. 处理Activity生命周期

NFC开发中最容易忽视的就是Activity的生命周期管理。如果没有正确处理,可能会导致各种奇怪的问题。这是我的经验之谈:

override fun onResume() { super.onResume() // 启用前台分发系统 nfcAdapter?.enableForegroundDispatch(this, pendingIntent, null, null) } override fun onPause() { super.onPause() // 禁用前台分发系统 nfcAdapter?.disableForegroundDispatch(this) }

特别注意要在onPause中禁用前台分发,否则可能会导致资源浪费和潜在的内存泄漏。我在早期项目中就因为这个疏忽,导致应用在后台时仍然响应NFC事件,消耗了大量电量。

6. 解析特定卡片数据

北京地铁卡使用的是Mifare Classic技术,要读取更多信息需要更深入的操作:

fun readMifareClassic(tag: Tag) { val mifare = MifareClassic.get(tag) try { mifare.connect() // 读取扇区0的数据 val auth = mifare.authenticateSectorWithKeyA(0, MifareClassic.KEY_DEFAULT) if (auth) { val blockData = mifare.readBlock(0) val dataStr = blockData.toHexString() // 处理读取到的数据... } } catch (e: Exception) { Log.e("NFC", "读取卡片失败", e) } finally { mifare.close() } }

这里有几个关键点:

  1. 必须先验证扇区密钥才能读取数据
  2. 不同扇区可能有不同的密钥
  3. 读取操作可能会抛出异常,必须做好错误处理

我曾经遇到过因为没处理异常导致应用崩溃的情况,特别是在用户快速移开卡片时。所以务必添加try-catch块。

7. 适配不同卡片类型

实际项目中,你可能会遇到各种类型的卡片。以下是一些常见卡片的处理方式:

  • ISO-DEP卡片(如银行卡):

    val isoDep = IsoDep.get(tag) isoDep.connect() val command = byteArrayOf(...) // APDU命令 val response = isoDep.transceive(command)
  • NFC-A卡片

    val nfcA = NfcA.get(tag) nfcA.connect() val atqa = nfcA.atqa // 获取ATQA值
  • NFC-B卡片

    val nfcB = NfcB.get(tag) nfcB.connect() val appData = nfcB.applicationData // 获取应用数据

每种卡片类型都有其特定的API和操作方式,建议在开发前先确定目标卡片的类型和技术规格。

8. 调试技巧与常见问题

在NFC开发过程中,我总结了一些实用的调试技巧:

  1. 获取卡片技术列表

    val techList = tag.techList Log.d("NFC", "支持的技术: ${techList.joinToString()}")

    这会告诉你检测到的卡片支持哪些技术,对排查问题很有帮助。

  2. 处理卡片超时: NFC操作通常都有超时限制,我建议添加超时处理:

    nfcA.timeout = 3000 // 设置3秒超时
  3. 多卡片冲突: 当同时有多张卡片在感应范围内时,可能会读取到错误数据。解决方法是在UI上提示用户"一次只放一张卡"。

  4. 低电量模式影响: 有些手机在省电模式下会限制NFC功能,这点需要告知用户。

我在一个商业项目中就遇到过用户反馈NFC时好时坏的问题,最后发现是因为他的手机开启了超级省电模式。现在我们的应用会在检测到NFC异常时主动检查这些系统设置。

9. 进阶功能实现

掌握了基础读取功能后,你可能还想实现更复杂的功能:

  1. 写入数据到NFC标签

    fun writeToTag(tag: Tag, data: String) { val ndef = Ndef.get(tag) ndef.connect() val message = NdefMessage(NdefRecord.createTextRecord("en", data)) ndef.writeNdefMessage(message) ndef.close() }
  2. Android Beam点对点传输

    nfcAdapter.setNdefPushMessageCallback({ event -> NdefMessage(NdefRecord.createTextRecord("en", "Hello NFC!")) }, this)
  3. 卡模拟模式: 这个需要手机硬件支持,且通常需要系统级权限,普通应用很难实现。

我在开发一个门禁系统时,就实现了通过NFC标签写入用户权限信息的功能。不过要注意,不是所有标签都可写,有些是只读的。

10. 性能优化与最佳实践

经过多个NFC项目的磨练,我总结出以下优化建议:

  1. 减少连接时间: NFC操作要快,尽量在最短时间内完成读写操作。我通常会把业务逻辑处理放到读取完成后进行。

  2. 缓存技术实例: 如果需要多次访问同一标签,可以缓存技术实例而不是每次都重新获取。

  3. 合理处理UI线程: NFC回调可能在非UI线程执行,记得用runOnUiThread更新界面。

  4. 电量优化: 在后台时禁用NFC监听,只在需要时才启用。

  5. 错误恢复机制: 实现自动重试逻辑,处理卡片短暂离开感应区的情况。

记得有次做公交卡余额查询功能时,因为没有优化读取流程,导致用户需要把卡片贴在手机上好几秒才能完成操作,体验很差。后来通过优化代码结构,将处理时间缩短到了1秒内。

http://www.jsqmd.com/news/1089340/

相关文章:

  • 从SHP到Excel,再到CAD:一站式地理数据格式转换与互操作实战指南
  • [智能体-589]:OpenClaw:HTML、JavaScript 、TypeScript、 Node.js、Python在智能体技术栈中各自的作用对比
  • Proteus原理图高效布线:标签与总线的进阶应用指南
  • OpenCore Legacy Patcher:三步让老旧Mac重获新生,体验最新macOS系统
  • 硬件原理图设计审查实战指南:从Checklist到高效协作
  • Linux内核页缓存覆写提权双链攻击深度剖析:CVE-2026-46331与CVE-2026-43503联动利用、检测与加固实战
  • 《淘宝订单API为什么个人账号调不通?企业认证+场景核验避坑指南》(附python源码)
  • 深入解析ChatGPT API的Token机制:从原理到精准计费实践
  • 【Linux】ClamAV实战:从零构建自动化病毒扫描与邮件告警系统
  • BetterGI 0.38.1版本安装失败终极解决方案:三步快速修复指南
  • QMCDecode:一键解锁QQ音乐加密格式,让音乐回归自由
  • 基于MCP协议与真实浏览器的AI驱动自动化测试实践
  • 原神帧率解锁技术方案:基于内存写入的安全高帧率实现
  • 从零到一:在VS2022中集成QT的实战环境配置
  • 如何用Python打造智能抢票神器:大麦网自动抢票脚本终极指南
  • CodeWarrior 调试实战:从断点到变量窗格的排错指南
  • 终极指南:如何用OneMore插件轻松实现OneNote全局搜索替换,告别手动修改烦恼!
  • 如何通过OneMore插件高效管理OneNote笔记:从基础编辑到智能组织实践指南
  • 【PyTorch】从ModuleNotFoundError到模型洞察:torchinfo安装、实战与避坑指南
  • 从手动到脚本:探索文件资源管理器(explorer)的优雅重启与状态恢复
  • EhViewer开源漫画应用:从零开始打造个性化漫画阅读体验的完整指南
  • 告别繁琐配置:基于Env与CLion的RT-Thread现代化开发环境一站式搭建
  • 抖音无水印下载终极指南:5分钟学会批量保存高清视频
  • Windows Cleaner:告别C盘爆红,让你的电脑重获新生
  • AMD Ryzen调试工具终极掌控:深度挖掘SMUDebugTool完全解锁指南
  • 大华DSS监控平台user_edit.action接口越权漏洞深度剖析与加固指南
  • OpenCore Legacy Patcher深度解析:老款Mac焕新终极指南
  • 广州图创interlib3系统sendMessage接口SQL注入漏洞深度剖析与修复
  • 基于STM32与Android的物联网环境监测APP开发实战
  • WarcraftHelper:魔兽争霸III在现代电脑上的5分钟完整解决方案