1. 为什么要在Uniapp里折腾NFC?
大家好,我是老张,一个在移动开发领域摸爬滚打了十来年的老兵。这些年,我见过太多开发者一听到“NFC”和“Uniapp”这两个词放在一起,第一反应就是摇头:“这能行吗?Uniapp不是搞前端的吗?” 我当初也是这么想的,直到一个真实的项目需求砸过来——客户需要一款用于内部资产盘点的App,要求员工用手机贴近资产标签(IC卡)就能自动读取并登记信息。如果走原生开发,安卓和iOS得各搞一套,成本和时间都扛不住。于是,我们硬着头皮在Uniapp里探索NFC的可能性。
实测下来,这条路不仅走得通,而且对于很多特定场景(比如门禁、工牌识别、物流标签读取)来说,性价比极高。Uniapp的跨端能力让你用一套代码覆盖安卓和iOS(尽管iOS的NFC权限更严格),而借助Android平台的Native.js能力,我们可以直接调用系统的NFC API,实现读取IC卡UID(唯一标识符)甚至扇区数据的功能。这篇文章,我就把自己踩过的坑、总结出来的最佳实践,手把手分享给你。无论你是想做一个简单的打卡应用,还是复杂的加密卡数据读取,相信都能找到可落地的方案。
2. 开发前的核心准备:权限、设备与原理
在动手写代码之前,有几件事必须门儿清,不然很容易做无用功。这就像做饭前,你得先看看厨房有没有锅和灶。
2.1 你的手机和IC卡“对话”吗?
首先,不是所有手机都有NFC功能。通常中高端机型配备得比较多。其次,更重要的是IC卡的类型。我们常说的IC卡,比如门禁卡、公交卡,大多属于Mifare Classic系列(简称M1卡),这也是我们这篇文章主要对付的目标。你的手机NFC模块必须支持读写这类卡的协议。好消息是,现在绝大部分安卓手机的NFC都兼容Mifare Classic。
如何确认呢?你可以先下载一个叫“NFC Tools”的App,用它贴近你的IC卡,如果能识别出类型是“Mifare Classic”或“NFC-A”,那就没问题。这一步千万别省,我见过有开发者代码写了一天,最后发现测试卡是ID卡(低频卡,手机NFC根本不支持),白白浪费了时间。
2.2 在Uniapp里给NFC开“通行证”
想让你的Uniapp应用访问手机的NFC硬件,必须在配置文件中声明权限。打开你项目根目录下的 manifest.json 文件,找到 app-plus -> distribute -> android 节点,在里面添加权限配置。光有权限声明还不够,对于安卓系统,尤其是比较新的版本,你还需要在应用启动时动态请求这些权限。
// manifest.json 部分配置
"app-plus": {
"distribute": {
"android": {
"permissions": [
"android.permission.NFC",
"android.permission.VIBRATE" // 可选,用于读卡成功时震动反馈
],
"features": [
"android.hardware.nfc" // 声明应用需要NFC硬件支持
]
}
}
}
2.3 NFC读卡的核心原理:前台调度系统
这是安卓平台NFC开发中最关键的一个概念,理解了它,代码就看懂了一半。想象一下,你的手机同时安装了支付宝、微信和你自己开发的App,都支持NFC。当用户贴上一张公交卡时,手机怎么知道该唤醒哪个应用呢?
安卓系统提供了一个叫**前台调度(Foreground Dispatch)**的机制。它允许你的应用在处于前台(即用户正在使用你的App界面)时,拥有最高的NFC事件优先级。你通过代码告诉系统:“嘿,我现在在前台,如果检测到NFC标签,先交给我来处理!” 这样,当IC卡贴近时,系统就会把标签信息直接发送给你的应用,而不是弹出应用选择器。
我们的核心代码,其实就是围绕如何启动和停止这个“前台调度”来展开的。在应用页面显示时启动它,在页面隐藏或应用暂停时关闭它,这样才能保证良好的用户体验和电量管理。
3. 从零开始:封装一个健壮的NFC工具类
直接在网上找的代码片段往往只解决了“读出来”的问题,但实际项目中,稳定性、错误处理和用户体验同样重要。下面我分享一个我优化过多次的NFC工具类,它包含了设备检测、前台调度管理、数据读取和基本的错误处理。
3.1 工具类骨架与初始化
我们创建一个单独的JS文件,比如 nfc.js,放在项目的 common 目录下。这样做的好处是业务逻辑和NFC底层调用分离,方便维护和复用。
// common/nfc.js
// 导入必要的Android类
let Context = plus.android.importClass("android.content.Context");
let NfcAdapter = plus.android.importClass("android.nfc.NfcAdapter");
let Intent = plus.android.importClass("android.content.Intent");
let PendingIntent = plus.android.importClass("android.app.PendingIntent");
let IntentFilter = plus.android.importClass("android.content.IntentFilter");
// 定义支持的NFC技术类型列表,覆盖常见的IC卡类型
const TECH_LISTS_ARRAY = [
["android.nfc.tech.IsoDep"],
["android.nfc.tech.NfcA"], // 绝大多数M1卡属于NfcA
["android.nfc.tech.NfcB"],
["android.nfc.tech.NfcF"],
["android.nfc.tech.NfcV"],
["android.nfc.tech.Ndef"],
["android.nfc.tech.NdefFormatable"],
["android.nfc.tech.MifareClassic"], // 关键!用于M1卡
["android.nfc.tech.MifareUltralight"]
];
const NfcManager = {
data: {
nfcAdapter: null,
mainActivity: null,
pendingIntent: null,
intentFilters: null,
isDispatchEnabled: false,
callback: null // 用于接收读卡结果
},
// 初始化:检查NFC并获取适配器
init: function() {
return new Promise((resolve, reject) => {
try {
this.data.mainActivity = plus.android.runtimeMainActivity();
let nfcManager = this.data.mainActivity.getSystemService(Context.NFC_SERVICE);
this.data.nfcAdapter = NfcAdapter.getDefaultAdapter(this.data.mainActivity);
if (!this.data.nfcAdapter) {
reject(new Error("设备不支持NFC功能"));

3796

被折叠的 条评论
为什么被折叠?



