无需Android Beam或HCE,Android能否实现NFC P2P通信以交换联系人?
解决方案:Android 14+ 实现NFC对等联系人交换(替代NameDrop)
方案1:NDEF标签双向读写模拟
核心思路是让两部设备同时开启NFC读写模式,碰一碰时互相读取对方模拟的NDEF标签,同时自身也提供包含联系人信息的NDEF数据,实现双向交换。
实现步骤:
- 注册NFC前台调度,过滤
NDEF_DISCOVERED和TECH_DISCOVERED意图,同时支持读写权限。 - 检测到设备触碰时,优先读取对方的NDEF消息获取联系人,同时将自身联系人写入本地模拟的NDEF标签供对方读取。
- 加入会话ID验证:交换前先传递随机生成的会话ID,仅当双方ID匹配时完成联系人导入,避免重复操作或读取无关标签。
- 注册NFC前台调度,过滤
核心代码片段:
// 初始化NFC适配器 val nfcAdapter = NfcAdapter.getDefaultAdapter(context) // 封装联系人信息为NDEF记录 val contactRecord = NdefRecord.createMime( "application/vnd.your.app.contact", Gson().toJson(localContact).toByteArray() ) val ndefMessage = NdefMessage(arrayOf(contactRecord)) // 设置前台调度 val pendingIntent = PendingIntent.getActivity( context, 0, Intent(context, MainActivity::class.java).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), PendingIntent.FLAG_MUTABLE ) val filters = arrayOf(IntentFilter(NfcAdapter.ACTION_NDEF_DISCOVERED).apply { addDataType("application/vnd.your.app.contact") }) val techLists = arrayOf(arrayOf(Ndef::class.java.name)) nfcAdapter.enableForegroundDispatch(activity, pendingIntent, filters, techLists) // 处理触碰事件 override fun onNewIntent(intent: Intent?) { super.onNewIntent(intent) if (NfcAdapter.ACTION_NDEF_DISCOVERED == intent?.action) { // 读取对方联系人 intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES)?.firstOrNull()?.let { msg -> val payload = (msg as NdefMessage).records.first().payload val receivedContact = Gson().fromJson(String(payload), Contact::class.java) importToContacts(receivedContact) } // 让对方读取自身联系人 writeLocalNdefMessage(ndefMessage) } }
- 权限配置(AndroidManifest.xml):
<uses-permission android:name="android.permission.NFC" /> <uses-feature android:name="android.hardware.nfc" android:required="true" /> <uses-permission android:name="android.permission.READ_CONTACTS" /> <uses-permission android:name="android.permission.WRITE_CONTACTS" />
方案2:LLCP底层协议实现
Android Beam虽被弃用,但NFC P2P的底层LLCP(Logical Link Control Protocol)仍可通过NfcAdapter的原生方法调用,实现双向套接字通信。
实现步骤:
- 双方同时尝试创建LLCP套接字,先以服务器角色监听,若超时则切换为客户端发起连接,自动适配主从角色。
- 连接建立后,双向传输联系人数据字节流。
- 通信完成后主动关闭套接字,释放资源。
核心代码片段:
// 初始化LLCP套接字 val socket = nfcAdapter.createLlcpSocket("com.your.app.nfc.service") try { socket.connect() val outputStream = socket.outputStream val inputStream = socket.inputStream // 发送本地联系人数据 outputStream.write(Gson().toJson(localContact).toByteArray()) outputStream.flush() // 接收对方联系人数据 val buffer = ByteArray(1024) val readSize = inputStream.read(buffer) val receivedContact = Gson().fromJson(String(buffer, 0, readSize), Contact::class.java) importToContacts(receivedContact) } catch (e: IOException) { // 连接失败则切换角色重试 switchToClientMode() } finally { socket.close() }
方案3:NFC+BLE fallback增强兼容性
针对部分不支持NFC P2P的设备,可添加BLE双向通信作为 fallback:
- 碰一碰时先尝试NFC交换,若超时或失败,自动触发BLE广播与扫描,配对后完成联系人交换。
- 保持操作流程一致,用户无需感知底层切换。
关键注意事项
- Android 13+需额外申请
POST_NOTIFICATIONS权限,用于向用户推送联系人导入提示。 - 长时间NFC扫描需使用前台服务,避免被系统后台回收。
- 加入重复交换拦截:通过设备ID+时间戳判断,短时间内同一设备仅完成一次交换。
内容的提问来源于stack exchange,提问作者Ladle
相关产品推荐
相关产品推荐

