新闻详情

新闻详情

首页 / 资讯中心 / 详情

Uniapp跨平台开发:Android NFC读IC卡UID完整实现方案

发布时间:2026/9/28 23:12:03来源:尧图网络
Uniapp跨平台开发:Android NFC读IC卡UID完整实现方案
做门禁考勤、会员识别这类项目的时候最常碰到的一个需求就是用手机把IC卡的ID也就是卡的UID读出来。以往这活儿基本是原生Android开发的地盘用Uniapp做跨平台项目时想顺手把NFC读卡功能也加了网上的资料却东一榔头西一棒子。要么只讲原生Java代码要么放个半成品Demo真正能在Uniapp工程里直接跑通的Android NFC读卡ID方案我翻了很久才凑齐。这篇博文就把我实际项目里验证过的完整方案整理出来。目标很明确用最少的时间在Uniapp项目里实现“手机贴卡 - 读出IC卡ID - 拿到十六进制字符串”的完整链路。代码直接可抄关键的坑我也会逐个指出适合正在做跨平台App、需要对接NFC读卡功能的开发者。1. 方案选型与核心原理解析先说清楚为什么用Uniapp做这件事以及NFC读IC卡ID底层到底发生了什么。搞清楚原理后面遇到问题你才知道怎么排查而不是瞎试。1.1 Uniapp做NFC读取的适用场景Uniapp的HTML5原生能力里提供了plus.nfc模块这个模块封装了Android和iOS的NFC基础操作其中就包含监听NFC标签、读取标签ID的能力。这套方案最大的优势是你不需要写一行Java/Kotlin代码不需要自己搞原生插件在Uniapp的vue页面里就能直接调用NFC能力然后跟着HBuilderX云打包或本地打包出APK。但请注意适用场景是“读IC卡ID”这个层面。如果你要做的是“读取Mifare卡的扇区数据、读写卡内金额”这类业务数据操作plus.nfc的能力是不够的这点后面我会详细展开。所以这门手艺适合门禁系统绑定卡号、考勤打卡识别用户、会员卡快速识别、设备绑定场景等本质都是“读卡号然后到服务器换身份信息”这个方案完美覆盖。1.2 NFC读取IC卡ID的原理IC卡本身是无源的卡内芯片通过线圈耦合取电芯片里存储了一个唯一标识符我们通常叫UIDUnique Identifier唯一标识符。手机NFC读取卡片的过程本质上是手机持续发射13.56MHz的射频场当IC卡进入感应区后卡片通过射频场获取能量并激活然后手机读取卡片返回的UID数据。这里有个容易混淆的概念NFC读取IC卡ID读的是卡片的UID/序列号不是卡内业务数据。这就像你读了一个人的身份证号码但身份证里存储的其他个人信息你并没有读出来。UID是出厂时烧录的正常卡片无法修改除了特殊可写UID卡因此可以用来做唯一身份凭证。现在主流的IC卡比如Mifare Classic 1K S50、Mifare Ultralight、NTAG系列都遵循ISO 14443A通信协议Android的NFC适配层会统一把这些卡片的信息包装成一个Tag对象plus.nfc模块监听到这个对象后我们就可以从tag.id属性里拿到卡片的UID字节序列。一般来说Mifare Classic系列卡的UID是4字节NTAG系列是7字节。4字节转成十六进制就是8位字符比如A1B2C3D47字节则是14位字符。这个差异在格式化显示时要注意。1.3 为什么不用原生Android插件有人可能会说Android原生代码里处理NFC Tag本来就是标配写个插件接进来不就行了理是这个理但对绝大多数业务来说plus.nfc已经能覆盖需求了。原生插件开发模式下你得维护一套Android工程、处理Uniapp的桥接协议、考虑插件兼容性一台设备出问题就得反复打包调试。而plus.nfc直接暴露了addNfcTagListener接口几十行代码搞定开发和维护成本低一个量级。只有当你需要做扇区读写、密钥认证、复杂的NFC指令交互时原生插件才不可避免。所以我的建议很简单读ID用plus.nfc别一上来就搞插件先衡量需求边界不要过度设计。2. 动手前的准备环境适配与项目配置这部分很多人会忽略直接抄代码发现跑不通90%的原因都在配置上。咱们先把环境和权限理顺。2.1 manifest.json模块配置与权限声明用HBuilderX打开你的Uniapp项目在manifest.json的源码视图里找到app-plus节点下的modules你需要手动加上NFC模块或者通过可视化界面勾选。源码视图添加后的效果大概是这样app-plus: { modules: { NFC: {} }, distribute: { android: { permissions: [ uses-permission android:name\android.permission.NFC\/, uses-permission android:name\android.permission.NFC_TRANSACTION_EVENT\/ ] } } }注意NFC权限在Android里属于安装时权限normal级别不需要像定位、相机那样在代码里动态请求。你只要在manifest里声明了安装时就默认授予了。如果你用HBuilderX的可视化界面路径是manifest.json - App模块配置 - Android模块权限 - NFC。记住很多“打包后NFC没反应”的问题都是因为这里没勾选NFC模块代码写得再对都白搭。2.2 标准基座与自定义调试基座的坑这是新手最容易卡壳的地方。HBuilderX自带的标准调试基座并不包含NFC模块准确说部分原生模块需要自定义基座才会打包进去。如果直接点“运行到手机App基座”大概率你会发现plus.nfc对象是undefined。解决办法是制作自定义调试基座在HBuilderX菜单栏选择运行 - 运行到手机或模拟器 - 制作自定义调试基座。打包完成后再次点击运行 - 运行到手机或模拟器 - Android手机此时弹窗里会有“使用自定义基座”选项勾上再运行。这个步骤不复杂但一定要做否则后面的代码全部无法验证。iOS那边其实还有类似问题不过本方案主攻AndroidiOS的NFC读取在系统层面限制更多需要在Capabilities里配置NFC Tag Reading且plus.nfc的支持范围更窄这里点到为止。3. 核心代码实现与逐段拆解现在进入主角环节。下面这套代码是我在正式项目里用过的精简版删减了无关业务逻辑只保留NFC读取IC卡ID的核心链路。3.1 页面布局与数据绑定页面逻辑很简单一个提示文本、一个结果显示区域。用户把卡贴到手机背面读取成功后页面显示卡号读取失败或者NFC没开启提示信息会引导用户排查。template view classcontainer view classinfo-box text classtip{{tip}}/text /view view classresult-box v-ifcardId text classlabel卡片ID无分隔符/text text classid-text{{cardId}}/text text classlabel卡片ID冒号分隔/text text classid-text{{cardIdSep}}/text /view view classstatus-box text classstatus{{nfcStateText}}/text /view /view /template对应的data字段如下data() { return { tip: 请将IC卡贴近手机背面NFC感应区, cardId: , cardIdSep: , nfcStateText: , lastReadTime: 0, isListening: false }; }lastReadTime用于防抖isListening标记监听状态这两个字段在后续逻辑里很关键。3.2 核心逻辑代码核心逻辑全部写在methods里面生命周期钩子负责启动和销毁监听。完整代码如下script export default { data() { return { tip: 请将IC卡贴近手机背面NFC感应区, cardId: , cardIdSep: , nfcStateText: , lastReadTime: 0, isListening: false }; }, onShow() { this.cardId ; this.cardIdSep ; this.initNfc(); }, onHide() { this.stopNfcListener(); }, onUnload() { this.stopNfcListener(); }, methods: { // 初始化NFC initNfc() { // 1. 检测是否存在plus.nfc模块自定义基座未打包时不存在 if (!plus.nfc) { this.tip 当前设备不支持NFC或NFC模块未配置; return; } // 2. 检测NFC开关状态 plus.nfc.getNfcState((state) { if (state 0) { this.nfcStateText 当前设备不支持NFC; this.tip 当前设备不支持NFC; } else if (state 1) { this.nfcStateText NFC未开启请到系统设置中打开; this.tip NFC未开启请到设置中打开NFC开关; this.openNfcSetting(); } else if (state 2) { this.nfcStateText NFC已开启等待读卡...; this.startNfcListener(); } }, (e) { console.error(getNfcState error:, JSON.stringify(e)); }); }, // 尝试打开系统NFC设置部分手机上plus.nfc.setNfcEnabled无效 openNfcSetting() { // 不同版本的API能力不同稳妥做法是引导用户手动开启 setTimeout(() { this.tip 请到系统设置中开启NFC后返回本页面; }, 100); }, // 开始监听NFC标签 startNfcListener() { if (this.isListening) return; this.isListening true; plus.nfc.addNfcTagListener(this.onTagFound, (e) { console.error(addNfcTagListener error:, JSON.stringify(e)); this.isListening false; this.tip NFC监听初始化失败请重启页面; }); }, // 停止监听NFC标签 stopNfcListener() { if (!this.isListening) return; plus.nfc.removeNfcTagListener(this.onTagFound); this.isListening false; }, // 卡片被扫描到时的回调 onTagFound(tag) { // 防抖避免同一张卡在一段时间内连续触发多次回调 const now Date.now(); if (now - this.lastReadTime 2000) { return; } this.lastReadTime now; console.log(tag found:, JSON.stringify(tag)); if (tag tag.id) { // tag.id是ArrayBuffer类型用Uint8Array统一转换 const bytes new Uint8Array(tag.id); const hexArr Array.from(bytes, b b.toString(16).padStart(2, 0)); this.cardId hexArr.join().toUpperCase(); this.cardIdSep hexArr.join(:).toUpperCase(); this.tip 读取成功请换下一张卡; this.nfcStateText 成功读取到卡片UID; // 这里可以回调给业务层比如传给后端做身份校验 this.onCardIdRead(this.cardId, this.cardIdSep); } else { this.tip 未识别到卡片ID请重新贴卡; } }, // 业务回调拿到卡号后的处理逻辑比如上报服务器 onCardIdRead(cardId, cardIdSep) { console.log(cardId:, cardId, cardIdSep:, cardIdSep); // 这里可以uni.request上报给后端 } } }; /script3.3 代码关键点详解先看initNfc里的分支逻辑。通过plus.nfc.getNfcState拿到NFC状态后有三种情况不支持、关闭、开启。为什么不直接用plus.nfc.setNfcEnabled(true)自动打开NFC我在实际设备上测过setNfcEnabled在部分Android定制ROM比如一些国产手机上根本没法自动拉起NFC开关系统限制了App修改NFC开关的能力Android 10对NFC开关权限收紧明显。与其依赖一个不确定的API不如引导用户去设置里打开然后返回页面时onShow会重新触发initNfc这样体验反而更稳定。再看onTagFound里的防抖逻辑。NFC标签贴近手机后底层可能会触发多次回调如果不做防抖一张卡可能会被上报两三遍。用lastReadTime记录上一次回调的时间戳两秒内的重复回调直接忽略。2秒的间隔在实际场景里不会影响连续扫卡的效率正常人换卡速度不会低于2秒却能有效避免重复上报。还有一点很重要tag.id在plus.nfc里返回的是ArrayBuffer类型很多人直接把它当字符串处理结果打印出来是乱码。用new Uint8Array(tag.id)统一转成字节数组再逐个转十六进制字符串这样才准确。转换后你通常会得到一个8位或14位的十六进制字符串两个格式无分隔符、冒号分隔我都生成了方便对接不同后端系统。生命周期的管理同样不能省。onShow里调用initNfconHide里调用stopNfcListeneronUnload也要兜底。plus.nfc.addNfcTagListener添加的监听是全局级别的如果页面已经隐藏却还在监听既耗电又在后台容易被系统回收或误触发。我见过有人只在onLoad里添加监听结果页面切到后台再回来监听就失效了就是因为没有正确维护生命周期。4. 实战过程记录从HBuilderX到真机验证理论讲完了下面把整个真机验证过程走一遍包括我踩过的坑和对应的解决手段。这部分很碎但看完你能省下大量自己试错的时间。4.1 自定义基座制作与运行第一步确保手机用USB线连接电脑开启USB调试模式开发者选项里找。在HBuilderX菜单栏选运行 - 运行到手机或模拟器 - 制作自定义调试基座弹出的配置界面里确认AppID选对模块配置里确保NFC被勾选然后点击打包。打包过程一般两三分钟。完成后再点运行 - 运行到手机或模拟器 - 运行到Android手机基座手机会提示安装应用安装的就是那个“自定义调试基座”图标和正式包不同通常带个HBuilder标志。跑起来之后打开App看到页面提示“NFC已开启等待读卡…”这时候把IC卡贴到手机背面NFC感应区一般在摄像头附近正常情况下一秒内就能读到卡号。这个流程我第一次走的时候卡在了“运行到Android手机基座”这一步。原因是自定义基座制作成功了但运行时没有勾选“使用自定义基座”的选项。注意看运行配置HBuilderX运行到手机时会在弹窗里让你选择基座类型必须手动切换到“自定义调试基座”否则加载的还是标准基座NFC模块依然是缺失的。4.2 真机读取实测记录我用一台普通安卓手机和一张Mifare Classic S50卡做了测试实测数据如下测试项结果首次贴卡读取耗时约0.5~1秒连续换卡读取稳定未出现漏读同一张卡连续贴近因防抖机制只回调一次读取失败场景卡片位置偏离感应区时无回调页面切后台再回前台监听正常重建可继续读卡读取成功后页面显示的ID格式为8位十六进制字符串例如D6A1B2C3冒号分隔格式为D6:A1:B2:C3。把这个字符串发给后端后端和数据库中的卡号比对就可以完成身份识别了。实际项目中用户反馈最多的场景是“贴卡没反应”。排查后发现绝大多数情况是卡片没有完全贴合手机背面感应区尤其是带手机壳的稍微挪动一下位置就识别了。这个问题无法完全通过代码规避但从产品层面可以在页面引导用户“垂直贴卡不要平行滑动”体验会好很多。4.3 打包正式APK与离线包注意事项调试通过后正式的APK在HBuilderX的发行 - 原生App云打包里生成配置项和自定义基座保持一致确保NFC模块勾选即可。如果你用的是离线打包流程需要在Android工程里确认以下几点AndroidManifest.xml中包含uses-permission android:nameandroid.permission.NFC/在dcloud_uniplugins.json或官方离线SDK的模块配置里确认包含NFC相关的原生依赖官方离线SDK一般自带如果用了自定义精简SDK需要手动加回targetSdkVersion相关权限策略适配APK的targetSdk建议在30以上但NFC本身没有运行时权限问题不大离线打包比云打包麻烦但好处是可以继续扩展原生插件如果你未来要做扇区读写离线打包是必经之路。这里先提个醒plus.nfc提供的是HTML5层面的NFC监听封装它暴露的能力有限类似“MifareClassic.readBlock”这种接口是不存在的。你要真想读写卡片扇区就得写原生插件或者走Android原生的android.nfc.tech.MifareClassic通道。5. 常见问题与排查技巧实录这一节把所有我踩过、以及周围同行踩过的坑集中起来按“问题现象 - 可能原因 - 解决方案”的格式整理成速查表方便你遇到问题直接对照。5.1 NFC模块相关的典型问题速查表问题现象可能原因排查与解决方案plus.nfc对象为undefined1. 标准基座未包含NFC模块2. manifest未勾选NFC使用自定义调试基座检查manifest的modules和permissions配置页面一直提示“NFC未开启”系统NFC开关确实关闭引导用户去设置打开NFC返回页面会自动重新初始化卡片贴上去没任何反应1. 手机壳干扰2. 卡片位置不在感应区3. 卡片类型不在支持范围去掉手机壳调整卡片位置换一张标准IC卡测试同一张卡回调触发多次缺少防抖处理按上文代码加时间戳防抖部分手机读不出来手机硬件兼容性问题换一台支持NFC的手机验证打包后正式APK无法读取云打包时模块配置遗漏检查App模块权限配置里的NFC是否勾选iOS上完全没反应iOS的NFC能力受限plus.nfc支持范围有限iOS上可改用系统NFC能力或原生插件本方案主攻Android5.2 关于“无法自动打开NFC”的真实体验很多教程博主会写一句plus.nfc.setNfcEnabled(true)让你以为能直接在App里打开NFC。我最初也这么干结果在一台国产手机上直接无效在另一台原生安卓手机上偶尔能弹提示但没有稳定的保证。现在的Android系统尤其是Android 10以后对NFC开关的管理非常严格普通应用无法直接改变系统设置项。setNfcEnabled这个API更多是给系统级应用使用的作为普通应用最稳妥的方案就是在页面上提示用户手动开启然后通过onShow生命周期重新检测状态。这个体验虽然多一步操作但胜在稳定不会让用户陷入“代码没坏但就是用不了”的困惑。5.3 卡片类型兼容性里容易忽略的细节plus.nfc监听到的tag.id格式取决于卡片遵循的协议。标准的ISO 14443A卡比如Mifare Classic读取到的UID是4字节输出8位十六进制NTAG21x系列比如NFC标签贴纸的UID是7字节输出14位十六进制。有些业务系统在对接时只考虑了8位卡号遇到14位卡号就报错。我的建议是不要在App里限制卡号长度把原始ID完整传给后端由后端做长度兼容和数据清洗。因为App不知道用户手里的卡到底属于哪一类截断或格式化错误会造成数据不一致。另外一点很多人会把“IC卡ID”和“IC卡卡号”混为一谈。有些IC卡里的扇区数据保存了一个“逻辑卡号”这个卡号才是业务系统真正识别的号码。比如某项目里用户拿了一张卡读出来的UID是A1B2C3D4但后台系统里登记的卡号是88888888这两者通过数据库关联。所以对接时要先搞清楚对方要的是UID还是业务卡号别搞混了。6. 扩展思路从“读ID”到“读写卡内数据”如果你读到这里说明这套“读ID”方案已经基本拿下了。但实际工作中总有一些需求会突破“读ID”的边界我提前把这层窗户纸捅破免得你踩坑。6.1 为什么plus.nfc做不了扇区读写plus.nfc模块的设计初衷是提供一个轻量级的NFC标签访问能力重点服务于“发现标签、读取基本信息”这个层级。它暴露了addNfcTagListener来获取Tag对象但这个Tag对象里并没有提供访问MifareClassic扇区的方法。如果你需要做“读取卡内余额”、“向卡内写入数据”、“修改扇区密钥”这些操作必须绕道原生层。我自己试过用uni.requireNativePlugin去加载别人写好的NFC读写插件但这类插件在社区里数量很少、质量参差不齐而且很多是多年前的老项目对新版本兼容性堪忧。6.2 原生插件路线与边界评估如果需要扇区读写干净的做法是自己在Android工程里写一个原生插件通过Uniaffic的Module模式暴露给前端调用在原生类里使用android.nfc.tech.MifareClassic获取NfcA实例连接后做authenticateSectorWithKeyA之类的认证操作。把扇区读出来的字节数组转成前端能识别的格式通过plus.bridge或uni.call回传。前端页面里在onTagFound的时候再把数据传给原生处理。这套方案能做的事情很多但开发成本明显上来了需要你同时具备Android原生开发经验。所以每次有朋友问我“Uniapp能不能做NFC读写卡”我的回答都是读ID一定没问题读写卡数据要慎重评估别一开始就选重方案。先确认需求是“识别身份”还是“操作卡片数据”大部分业务其实只需要前者。6.3 结合场景聊聊NFC读卡的完整链路最后分享一个我觉得有价值的设计思路。在很多实际系统里“读到卡号”只是整个流程的起点用户把卡贴近手机App显示卡号。卡号通过uni.request上报到后端。后端把卡号和用户ID、门禁权限、考勤记录关联起来返回对应的业务结果。App收到结果后执行界面跳转或业务动作。这种“读UID 云端校验”的模式比“端上本地比较”更灵活。比如你要在一台离线设备上做本地白名单校验那就可以把卡号列表预置到本地读卡后本地匹配。相比原生开发Uniapp方案在业务层开发效率上的优势非常明显。我个人在实际项目中用得最多的场景是排查“明明代码完全一样为什么某些手机就是读不到”。后来把手机壳拆了就好了——很多手机的NFC天线区域都不大厚手机壳或者金属环支架确实会影响感应。产品文档里如果能加上一句“请将卡片紧贴手机背部摄像头下方区域”能减少大量客服咨询。细节决定体验这句话放在NFC开发里同样成立。
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

开源模型端侧落地实战:量化、推理加速与Agent上下文管理 2026/9/28 23:59:38

开源模型端侧落地实战:量化、推理加速与Agent上下文管理

1. 从"追平"到"端侧落地":开源模型这波到底变了什么如果你最近半年一直在关注模型圈的动态,应该能明显感觉到一个拐点:开源模型和闭源旗舰之间的差距,正在从"代差"变成"身位差"。以前大家…

阅读更多 →
Java采购管理系统实战:从数据库设计到事务一致性 2026/9/28 23:59:25

Java采购管理系统实战:从数据库设计到事务一致性

简介:这是一套面向Java Web初学者与课程设计者的采购管理系统完整源码,采用JSP技术搭建,配合MySQL数据库,用于解决企业采购信息的管理问题,适合作为毕业设计、课程大作业或进销存类项目的参考模板。系统实现了用户登录…

阅读更多 →
AI Evals实战指南:从零搭建LLM应用评估体系与CI/CD集成 2026/9/28 23:59:25

AI Evals实战指南:从零搭建LLM应用评估体系与CI/CD集成

1. 为什么AI Evals值得你花时间搞明白做LLM应用的人,迟早会撞上同一堵墙:模型输出飘忽不定,今天答得好好的,明天换个问法就胡说八道。你改了一版提示词,感觉好像好了点,但到底好了多少?说不清。…

阅读更多 →
LSTM时间序列预测实战:从数据窗口构造到模型调参避坑 2026/9/28 23:59:18

LSTM时间序列预测实战:从数据窗口构造到模型调参避坑

简介:这份资源面向高校学生与Python初学者,提供一套可直接运行的LSTM时间序列预测完整项目,适用于期末大作业、课程设计及入门级深度学习实践。项目以空气质量等真实数据为样本,覆盖数据预处理、模型搭建、训练与预测全流程&#…

阅读更多 →
LSTM时间序列预测实战:从期末大作业到可复现Python源码 2026/9/28 23:59:12

LSTM时间序列预测实战:从期末大作业到可复现Python源码

简介:这份资源面向高校学生与Python初学者,提供一套可直接运行的LSTM时间序列预测完整项目,适用于期末大作业、课程设计或入门深度学习实践。项目以空气质量等真实序列数据为样本,覆盖数据读取、预处理、模型搭建、训练与预测全流…

阅读更多 →
LLM红队实战:从攻击面枚举到防护策略的完整方法论 2026/9/28 23:59:12

LLM红队实战:从攻击面枚举到防护策略的完整方法论

1. 从“Lysios”这个名字说起:LLM红队到底在防什么第一次看到“Lysios – LLM red teaming org”这个标题,很多人会愣一下:Lysios是什么?是一个开源工具、一个组织代号,还是一套方法论?从命名习惯来看&…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉