Android HCE 模拟 NFC 标签实现跨设备 NDEF 数据共享
2026/9/19 6:54:30 网站建设 项目流程

NFC 这玩意儿在 Android 上一直有点"薛定谔"的味道——你永远不知道用户手里那台设备到底能不能稳定读到你的标签。我最早接触 NFC 是在做门禁卡模拟的项目里,当时用一台旧手机写 NDEF 数据,换台设备就读不出来,排查了半天才发现是标签容量和 NDEF 格式的兼容性问题。后来接触到 HCE(Host Card Emulation,主机卡模拟),才意识到 Android 从 4.4 开始就已经把"手机变成一张卡"这件事做成了系统级能力,只是大多数人只知道用它来做支付,很少有人拿它来做设备间的数据共享。

这篇内容就是围绕Android HCE 模拟 NFC 标签、实现跨设备 NDEF 数据共享这个主题展开的。我会从 HCE 的底层机制讲起,把 APDU 指令的交互流程拆开揉碎,然后给出一个可以直接跑通的完整实现方案,最后聊聊我在实际调试中踩过的那些坑。适合有一定 Android 基础、想深入理解 NFC 通信原理的开发者,也适合做物联网设备配网、身份凭证传递、离线数据交换这类场景的同行参考。

1. 先搞清楚 HCE 到底在模拟什么

1.1 从物理标签到软件模拟的本质区别

要理解 HCE,得先明白普通 NFC 标签是怎么工作的。一张 NTAG213 或者 Mifare Classic 的标签,本质上就是一块带天线的存储芯片,读卡器发出 13.56MHz 的载波信号,标签通过电磁感应取电,然后把存储区里的数据以特定格式回传。整个过程里,标签是被动的,它没有 CPU,没有操作系统,只是按照预设的协议响应读卡器的指令。

HCE 做的事情,是把这块"存储芯片"用软件来模拟。手机里的 NFC 控制器收到读卡器的指令后,不再去访问物理标签,而是把指令转发给操作系统里的一个服务,由这个服务来决定怎么响应。这个服务在 Android 里就是HostApduService,它跑在应用进程里,你可以用任意代码来生成响应数据。

这个区别带来的最大好处是:数据是动态的。物理标签写进去什么就是什么,而 HCE 可以根据时间、位置、用户状态、后端下发的数据来实时生成响应内容。比如你可以做一个"动态凭证"系统,每次读卡返回的 NDEF 数据都不一样,这在物理标签上是不可能实现的。

但代价也很明显:HCE 依赖手机的电量和 NFC 控制器状态,响应速度受系统调度影响,而且不是所有 Android 设备都支持——虽然现在支持率已经很高了,但一些低端机或者定制 ROM 仍然可能阉割掉这个功能。

1.2 HCE 与 SE、eSE 的三角关系

Android 的 NFC 架构里,卡模拟有三种模式:基于安全单元(SE/eSE)的卡模拟基于主机的卡模拟(HCE),以及基于 SIM 卡的卡模拟。这三者的核心区别在于"密钥和敏感数据存在哪里"。

SE 方案里,数据存在一颗独立的安全芯片里,这颗芯片通过了各种安全认证,能抵抗物理攻击和侧信道攻击,所以银行支付类应用基本都用这个。但 SE 的访问权限被运营商或者手机厂商牢牢把控,普通开发者根本拿不到。

HCE 方案里,数据存在应用进程的内存或者本地存储里,安全性完全靠应用自己保证。Android 用了一个叫AID(Application ID)路由的机制来把读卡器的指令分发到对应的应用。每个 HCE 应用在 Manifest 里声明自己支持的 AID 列表,当读卡器发送 SELECT AID 指令时,系统会查找匹配的应用并把后续的 APDU 都转发给它。

这里有个关键点:AID 路由是有优先级的。如果多个应用声明了同一个 AID,系统会按照用户设置的默认应用或者安装顺序来决定。而且从 Android 10 开始,后台启动的 HCE 服务会被限制,必须在前台或者有前台服务的情况下才能正常响应。

1.3 NDEF 在 HCE 场景下的特殊处理

NDEF(NFC Data Exchange Format)是 NFC 论坛定义的一种数据封装格式,一条 NDEF 消息可以包含多个 NDEF 记录,每条记录有类型、ID、负载等字段。物理标签通常会把 NDEF 消息直接存在存储区里,读卡器读取时按照 TLV 格式解析。

但在 HCE 场景下,读卡器并不知道对面是物理标签还是手机,它只会按照 NFC Forum 定义的标准流程来操作。对于 Type 4 Tag(也就是 ISO-DEP 协议上的 NDEF 标签),读卡器会先发送 SELECT AID 指令选择 NDEF 应用(AID 是D2760000850101),然后通过一系列 APDU 来读取 Capability Container、NDEF 数据等。

所以 HCE 模拟 NDEF 标签的核心工作,就是在 HostApduService 里正确响应这些标准 APDU 指令,让读卡器以为自己在跟一张真实的 Type 4 Tag 通信。这比简单地返回一段数据要复杂得多,因为你需要实现完整的状态机。

2. APDU 指令交互的完整拆解

2.1 Type 4 Tag 的 APDU 指令集

要让 HCE 模拟的标签被读卡器正确识别,必须实现 Type 4 Tag 规范里定义的那几条核心 APDU。我把它们整理成了一张表,方便对照:

指令名称CLAINSP1P2功能说明
Select NDEF App00A40400选择 NDEF 应用,数据域为 AID
Select CC File00A4000C选择 Capability Container 文件
Read CC00B00000读取 CC 文件内容
Select NDEF File00A4000C选择 NDEF 数据文件
Read NDEF00B00000读取 NDEF 数据
Write NDEF00D60000写入 NDEF 数据(可选)

每条指令的响应都包含两部分:数据域状态字(SW1 SW2)。状态字90 00表示成功,6A 82表示文件未找到,6D 00表示指令不支持。读卡器会根据状态字来判断下一步操作。

2.2 Capability Container 的构造逻辑

CC 文件是 Type 4 Tag 的"身份证",它告诉读卡器这张标签的容量、支持的协议版本、NDEF 文件的位置等信息。CC 文件固定 15 字节,格式如下:

字节 0-1: CCLEN (CC 文件长度,固定 0x000F) 字节 2: 映射版本 (0x20 表示版本 2.0) 字节 3-4: MLe (最大读长度,通常 0x003B 即 59 字节) 字节 5-6: MLc (最大写长度,通常 0x0034 即 52 字节) 字节 7: TLV 类型 (0x04 表示 NDEF 文件控制 TLV) 字节 8-9: NDEF 文件长度 (最大可存储的 NDEF 消息字节数) 字节 10: NDEF 文件 ID (0x00) 字节 11-12: 读访问权限 (0x00 表示自由读) 字节 13-14: 写访问权限 (0x00 表示自由写,0xFF 表示只读)

这里有个容易踩的坑:MLe 和 MLc 的值会影响读卡器的行为。如果 MLe 设得太小,读卡器会分多次读取,增加交互轮次;如果设得太大,又可能超出某些读卡器的缓冲区。我实测下来,MLe 设为 0x003B、MLc 设为 0x0034 是比较稳妥的选择,兼容性最好。

NDEF 文件长度字段决定了读卡器认为这张标签能存多少数据。你可以把它设得比实际需要的大一些,但不要超过 0xFFFE,否则某些读卡器会解析异常。

2.3 NDEF 消息的 TLV 封装

NDEF 文件本身不是裸的 NDEF 消息,而是用 TLV(Type-Length-Value)格式封装的。对于 NDEF 数据,TLV 类型是0x04,后面跟两个字节的长度(大端序),然后是 NDEF 消息本体。最后还要加一个0xFE作为终止 TLV。

举个例子,如果 NDEF 消息是D1 01 0A 55 04 65 78 61 6D 70 6C 65 2E 63 6F 6D(一条 URI 记录),那么完整的 NDEF 文件内容就是:

04 00 10 D1 01 0A 55 04 65 78 61 6D 70 6C 65 2E 63 6F 6D FE

其中04是 TLV 类型,00 10是长度(16 字节),后面是 NDEF 消息,最后FE是终止符。

在 HCE 的 processCommandApdu 方法里,你需要根据读卡器发来的指令,动态构造这些响应数据。读卡器可能会分多次读取 NDEF 文件,所以你的服务需要维护一个"当前读取偏移量"的状态。

3. 从零搭建一个可用的 HCE NDEF 服务

3.1 项目配置与 AID 注册

先在 Android Studio 里新建一个项目,minSdkVersion 建议设为 21(Android 5.0),因为 HCE 的完整 API 从 19 开始支持,但 21 以上的兼容性更好。

AndroidManifest.xml里注册服务:

<service android:name=".MyHceService" android:exported="true" android:permission="android.permission.BIND_NFC_SERVICE"> <intent-filter> <action android:name="android.nfc.cardemulation.action.HOST_APDU_SERVICE" /> </intent-filter> <meta-data android:name="android.nfc.cardemulation.host_apdu_service" android:resource="@xml/apduservice" /> </service>

然后在res/xml/apduservice.xml里声明 AID:

<host-apdu-service xmlns:android="http://schemas.android.com/apk/res/android" android:description="@string/service_desc" android:requireDeviceUnlock="false"> <aid-group android:description="@string/aid_group_desc" android:category="other"> <aid-filter android:name="D2760000850101" /> </aid-group> </host-apdu-service>

这里的D2760000850101就是 NFC Forum 定义的 NDEF Type 4 Tag 应用 AID。requireDeviceUnlock设为 false 表示锁屏状态下也能响应,如果你做的是支付类应用,建议设为 true。

注意:从 Android 10 开始,如果应用在后台,HCE 服务可能无法被唤醒。你需要确保应用在前台,或者启动一个前台服务来维持 HCE 的可用性。

3.2 HostApduService 的核心实现

服务类需要继承HostApduService,重写processCommandApduonDeactivated两个方法。核心逻辑是根据 CLA 和 INS 来分发处理:

public class MyHceService extends HostApduService { private static final byte[] NDEF_AID = hexToBytes("D2760000850101"); private static final byte[] CC_FILE = hexToBytes("000F20 003B 0034 04 00FF 00 00 00 00"); private byte[] ndefFile; private int readOffset = 0; @Override public byte[] processCommandApdu(byte[] apdu, Bundle extras) { if (apdu == null || apdu.length < 4) { return hexToBytes("6D00"); } byte cla = apdu[0]; byte ins = apdu[1]; byte p1 = apdu[2]; byte p2 = apdu[3]; if (cla == 0x00 && ins == (byte) 0xA4) { return handleSelect(apdu); } else if (cla == 0x00 && ins == (byte) 0xB0) { return handleReadBinary(apdu); } else if (cla == 0x00 && ins == (byte) 0xD6) { return handleUpdateBinary(apdu); } return hexToBytes("6D00"); } private byte[] handleSelect(byte[] apdu) { if (apdu.length < 5) return hexToBytes("6A82"); int aidLen = apdu[4] & 0xFF; if (apdu.length < 5 + aidLen) return hexToBytes("6A82"); byte[] aid = Arrays.copyOfRange(apdu, 5, 5 + aidLen); if (Arrays.equals(aid, NDEF_AID)) { readOffset = 0; return hexToBytes("9000"); } return hexToBytes("6A82"); } private byte[] handleReadBinary(byte[] apdu) { int offset = ((apdu[2] & 0xFF) << 8) | (apdu[3] & 0xFF); int le = apdu.length > 4 ? (apdu[4] & 0xFF) : 0; byte[] source = (offset < CC_FILE.length) ? CC_FILE : ndefFile; int realOffset = (offset < CC_FILE.length) ? offset : offset - CC_FILE.length; if (realOffset >= source.length) return hexToBytes("6B00"); int len = Math.min(le == 0 ? source.length - realOffset : le, source.length - realOffset); byte[] data = Arrays.copyOfRange(source, realOffset, realOffset + len); return concat(data, hexToBytes("9000")); } }

这段代码里有个细节需要注意:CC 文件和 NDEF 文件是分开的两个"文件",读卡器会先 SELECT CC 文件读取能力信息,再 SELECT NDEF 文件读取实际数据。我在代码里用 offset 是否小于 CC_FILE.length 来区分,这是一种简化处理,更严谨的做法是维护一个当前选中文件的状态变量。

3.3 NDEF 消息的构造与动态更新

构造 NDEF 消息可以用 Android 自带的NdefRecordNdefMessage类,非常方便:

public void updateNdefData(String text) { NdefRecord record = NdefRecord.createTextRecord("zh", text); NdefMessage message = new NdefMessage(new NdefRecord[]{record}); byte[] ndefBytes = message.toByteArray(); // 封装 TLV ByteArrayOutputStream bos = new ByteArrayOutputStream(); bos.write(0x04); bos.write((ndefBytes.length >> 8) & 0xFF); bos.write(ndefBytes.length & 0xFF); bos.write(ndefBytes); bos.write(0xFE); ndefFile = bos.toByteArray(); }

如果你想让数据动态变化,可以在每次processCommandApdu被调用时重新生成 NDEF 消息。比如根据当前时间生成一个动态令牌,或者从后端拉取最新数据。但要注意,不要在 processCommandApdu 里做耗时操作,因为这个方法是在 Binder 线程里同步执行的,超过几秒不响应读卡器就会超时。

我的做法是提前在后台线程准备好数据,processCommandApdu 只负责读取内存里的缓存。如果需要实时拉取,可以用一个短超时的本地缓存,或者返回上一次的数据并异步更新。

4. 跨设备调试中那些让人抓狂的问题

4.1 读卡器兼容性:为什么同一张"卡"有的设备读得到有的读不到

这是我在调试中遇到最多的问题。同样的 HCE 服务,小米手机能读到,三星读不到;或者同一个读卡器,读物理标签正常,读 HCE 就失败。排查下来主要有几个原因:

第一是 AID 匹配问题。有些读卡器在 SELECT AID 之后,还会发送一些厂商自定义的指令来探测标签类型。如果你的服务对这些指令返回6D00,读卡器可能就直接放弃了。解决办法是在 processCommandApdu 里对未知指令返回9000加空数据,而不是错误码,让读卡器继续走标准流程。

第二是时序问题。HCE 的响应延迟比物理标签高得多,物理标签通常在几毫秒内响应,而 HCE 可能需要几十毫秒甚至上百毫秒。有些读卡器的超时设置比较短,就会认为标签不存在。这个只能通过优化代码来缓解,比如把 NDEF 数据预先生成好,避免在响应时做计算。

第三是 NDEF 文件长度字段的设置。我遇到过一种情况,CC 文件里声明的 NDEF 文件长度是 0x00FF,但实际返回的数据超过了这个长度,读卡器解析时就出错了。一定要确保声明的长度和实际数据一致。

4.2 状态机维护:读卡器分片读取时的偏移量处理

Type 4 Tag 的读取是分片的,读卡器会发送多次 READ BINARY 指令,每次读取一部分数据。如果你的服务没有正确维护读取偏移量,就会返回错误的数据。

我一开始的实现是每次 READ BINARY 都从头返回数据,结果读卡器读到的 NDEF 消息总是被截断。后来改成根据 APDU 里的 P1P2 字段来计算偏移量,才解决了问题。P1 是高字节,P2 是低字节,组合起来就是 16 位的偏移地址。

但这里还有个坑:CC 文件和 NDEF 文件的偏移量是独立的。读卡器读取 CC 文件时偏移量从 0 开始,读取 NDEF 文件时偏移量也从 0 开始。所以你不能用一个全局的 readOffset,而要根据当前选中的文件来分别维护。

4.3 锁屏与后台限制:Android 版本差异带来的行为变化

Android 各个版本对 HCE 的后台限制越来越严格。在 Android 9 及以前,只要服务注册了,锁屏状态下也能响应。但从 Android 10 开始,如果应用在后台且没有前台服务,HCE 可能会被系统挂起。

我实测下来,最稳妥的方案是:在应用启动时检查 NFC 权限和 HCE 可用性,然后启动一个前台服务来维持 HCE 的活跃状态。前台服务需要显示一个通知,虽然用户体验上有点打扰,但这是目前唯一可靠的做法。

另外,requireDeviceUnlock这个配置项也要注意。设为 false 时锁屏可用,但某些 ROM 会忽略这个设置强制要求解锁。设为 true 时则必须解锁才能响应,适合安全性要求高的场景。

4.4 用另一台手机做读卡器时的特殊处理

调试 HCE 最方便的方式是用另一台支持 NFC 的手机做读卡器。Android 提供了NfcAdapter.enableReaderMode方法,可以让你在应用里直接读取 NFC 标签,包括 HCE 模拟的标签。

Bundle options = new Bundle(); options.putInt(NfcAdapter.EXTRA_READER_PRESENCE_CHECK_DELAY, 250); nfcAdapter.enableReaderMode(this, new NfcAdapter.ReaderCallback() { @Override public void onTagDiscovered(Tag tag) { Ndef ndef = Ndef.get(tag); if (ndef != null) { try { ndef.connect(); NdefMessage message = ndef.getNdefMessage(); // 处理读取到的 NDEF 消息 ndef.close(); } catch (Exception e) { Log.e(TAG, "读取失败", e); } } } }, NfcAdapter.FLAG_READER_NFC_A | NfcAdapter.FLAG_READER_SKIP_NDEF_CHECK, options);

注意FLAG_READER_SKIP_NDEF_CHECK这个标志,加上它可以让读卡器跳过 NDEF 格式检查,直接返回原始数据。这在调试阶段很有用,因为如果 NDEF 格式有问题,不加这个标志的话读卡器可能直接不回调。

还有一个经验:两台手机贴在一起时,NFC 天线位置很关键。不同手机的天线位置不一样,有的在背面顶部,有的在中间。我调试时经常要把两台手机反复调整位置才能触发,建议先用物理标签确认读卡器的天线位置,再对准 HCE 手机的天线。

5. 把 HCE 用在真实场景里的几个思路

5.1 设备配网:用 NFC 传递 Wi-Fi 凭证

这是 HCE 最实用的场景之一。智能家居设备通常没有屏幕和键盘,配网很麻烦。如果设备端带 NFC 读卡器,手机端用 HCE 模拟一张 NDEF 标签,里面包含 Wi-Fi 的 SSID 和密码,设备一碰就能读取并自动连接。

NDEF 消息可以用NdefRecord.createMime("application/vnd.wfa.wsc", wifiConfigBytes)来构造,其中 wifiConfigBytes 是 Wi-Fi 配置的二进制格式。不过这个格式比较复杂,实际实现时建议用 Android 的WifiConfiguration或者WifiNetworkSpecifier来生成配置数据。

这个方案的好处是不需要设备端有屏幕,也不需要手机装额外的 App(如果做成系统级功能的话)。而且 NFC 的通信距离很短(通常几厘米),天然具有"物理接近"的安全性,不会像蓝牙那样被远程嗅探。

5.2 身份凭证:动态令牌的离线验证

HCE 可以模拟一张"动态卡",每次读取时返回不同的数据。比如你可以实现一个基于时间的一次性密码(TOTP)系统,NDEF 消息里包含当前时间窗口的令牌值。读卡器端拿到令牌后,用相同的密钥和算法验证,就能确认身份。

这种方案适合门禁、考勤、活动签到等场景。相比静态的物理卡,动态令牌更难被复制,因为每次读取的值都不一样。但要注意,HCE 不能完全替代安全芯片,因为密钥存在应用进程里,root 过的设备或者被调试的应用都可能泄露密钥。如果安全性要求很高,还是得用 SE 方案。

5.3 数据交换:两台设备之间的离线传输

两台都支持 HCE 和读卡器模式的设备,可以通过 NFC 交换数据。一台开 HCE 模拟标签,另一台开读卡器模式读取。这种方式不需要网络,不需要配对,碰一下就能传数据。

传输的数据量受限于 NFC 的速率和 NDEF 消息的大小。Type 4 Tag 的 NDEF 文件最大可以到 64KB 左右,但实际传输时受读卡器缓冲区限制,通常单次传输几 KB 比较稳妥。如果要传大文件,可以分多次读取,每次读一部分,但这样交互轮次会很多,用户体验不好。

我的建议是:NFC 只用来传递"连接信息",比如蓝牙地址、Wi-Fi 直连的 SSID 和密码,实际的大数据传输走蓝牙或者 Wi-Fi。这样既利用了 NFC 的便捷性,又避开了它的速率瓶颈。

5.4 调试工具:用 HCE 模拟各种标签做测试

如果你在做 NFC 读卡器端的开发,HCE 是一个非常好的测试工具。你可以用 HCE 模拟各种类型的标签,测试读卡器对不同 NDEF 格式、不同容量、不同协议的兼容性。这比买一堆物理标签要方便得多,而且可以随时修改数据。

我通常会做一个"调试模式"的 HCE 应用,里面预置几种常见的标签配置:空标签、只读标签、大容量标签、格式错误的标签等。测试读卡器时切换不同的配置,就能快速定位兼容性问题。

6. 一些不那么显然的实操心得

6.1 NDEF 消息的字节序和长度字段

NDEF 记录里的长度字段有大端序和小端序的区别,具体取决于记录类型。短记录(SR=1)的长度字段是 1 字节,普通记录是 4 字节大端序。TLV 的长度字段是 2 字节大端序。这些细节如果搞错了,读卡器解析出来的数据就是乱的。

我建议在构造 NDEF 消息时,尽量用 Android 提供的NdefMessage.toByteArray()方法,它会自动处理这些格式。只有在需要手动构造特殊格式时才自己拼字节,而且一定要用十六进制工具验证输出。

6.2 服务被系统回收后的恢复

HostApduService 是一个普通的 Android Service,系统在内存紧张时可能会回收它。如果服务被回收了,HCE 就会失效,直到应用重新启动。这个问题在低内存设备上特别明显。

解决办法是在onDeactivated回调里检查服务状态,如果是因为系统回收导致的 deactivate,可以尝试重新启动服务。但更可靠的做法还是用前台服务,让系统知道这个服务很重要,不要轻易回收。

6.3 不同 ROM 的兼容性差异

国内各大厂商的 ROM 对 NFC 和 HCE 的支持程度差异很大。我实测下来,原生 Android 和小米、三星的兼容性最好,华为和 OPPO 在某些版本上会有问题,特别是一些定制化的省电策略会限制 HCE 的后台响应。

如果你的应用需要覆盖多种设备,建议在代码里加一些兼容性检测和降级处理。比如检测到 HCE 不可用时,提示用户手动开启 NFC 或者切换到其他交互方式。

6.4 测试时的一个小技巧

调试 HCE 时,我经常需要反复修改代码然后测试。如果每次都重新安装应用,很浪费时间。我的做法是在应用里加一个"重新加载"的按钮,点击后重新生成 NDEF 数据并重启 HCE 服务。这样不用重新安装就能测试不同的数据配置。

另外,用adb logcat过滤HostApduService相关的日志,可以看到系统分发的 APDU 指令和你的响应,对排查问题非常有帮助。我通常会把 processCommandApdu 的输入输出都打上日志,这样一眼就能看出是哪条指令出了问题。

6.5 关于 NDEF 写入的注意事项

虽然 Type 4 Tag 规范支持写入 NDEF 数据,但在 HCE 场景下,写入操作需要特别小心。因为 HCE 服务是无状态的,每次读卡器连接都是一个新的会话,你写入的数据需要持久化到本地存储,否则下次读取就丢了。

而且写入操作涉及权限控制,如果 CC 文件里声明了写保护,读卡器就无法写入。我一般建议在 HCE 场景下把标签设为只读,数据由应用自己管理,通过其他方式(比如应用内设置或者后端下发)来更新。

6.6 性能优化的几个方向

HCE 的响应速度直接影响用户体验。我做过一些优化,效果比较明显的有:预生成 NDEF 字节数组,避免在 processCommandApdu 里做序列化;用静态变量缓存 CC 文件和 NDEF 文件,避免重复构造;减少日志输出,特别是在生产版本里关掉详细日志。

还有一个容易被忽略的点:processCommandApdu 返回的字节数组不要太大。虽然理论上可以返回几 KB 的数据,但实际测试中,超过 256 字节的响应在某些读卡器上会出现问题。如果需要传输大块数据,还是分片读取比较稳妥。

7. 从 HCE 延伸出去的一些思考

HCE 这个技术本身并不新,但它的应用场景一直在扩展。除了前面提到的配网、凭证、数据交换,我还见过用它来做电子票务、会员卡、设备配对等场景。核心思路都是一样的:把手机变成一张可以动态生成内容的卡

但 HCE 也有它的边界。它不适合做高安全性的支付,因为密钥保护不如 SE;不适合做大数据量传输,因为 NFC 速率有限;不适合做需要长时间保持连接的应用,因为 NFC 是短连接协议。理解这些边界,才能在对的场景用对的技术。

我在实际项目里,通常会把 HCE 和其他技术组合使用。比如用 HCE 传递蓝牙配对信息,然后切换到蓝牙做实际的数据传输;或者用 HCE 做身份验证,验证通过后切换到 Wi-Fi 做后续通信。这种"NFC 触发 + 其他通道传输"的模式,在实践中效果很好。

最后分享一个我在调试中总结的小经验:每次修改 HCE 相关代码后,最好把手机的 NFC 开关关掉再打开。因为 Android 的 NFC 服务会缓存 AID 路由信息,有时候代码改了但路由没更新,导致调试结果和预期不符。这个坑我踩过好几次,后来养成了改代码就重启 NFC 的习惯,省了不少排查时间。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询