Android设备NFC TAG读取实现问题:跨设备写入失败排查
NFC设备间文本传输失败排查
问题背景
实现了一个Android Activity,用于接收另一安装了指定NFC短信应用的设备发送的纯文本消息,已添加Manifest权限和Intent过滤器,但对方设备写入应用报错,无法完成NFC写入。
实现代码
const val MIME_TEXT_PLAIN = "text/plain" class MainActivity : AppCompatActivity() { private val TAG = MainActivity::class.java.simpleName private lateinit var binding: ActivityMainBinding private var nfcAdapter: NfcAdapter? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = ActivityMainBinding.inflate(layoutInflater) setContentView(binding.root) // 初始化NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(this) // 检查设备是否支持NFC if (nfcAdapter == null) { Log.w(TAG, "NFC non supportato!") binding.nfcStatusText.text = "No NFC on this device." } else { // 设置NFC按钮点击监听 binding.nfcButton.setOnClickListener { enableNfc() } } } private fun enableNfc() { Log.w(TAG, "Abilito l'NFC...") // 检查NFC是否启用 if (!nfcAdapter?.isEnabled!!) { binding.nfcStatusText.text = "Turn On NFC" } else { binding.nfcStatusText.text = "Searching..." } } override fun onNewIntent(intent: Intent) { super.onNewIntent(intent) receiveMessageFromDevice(intent) } override fun onResume() { super.onResume() enableForegroundDispatch(this, this.nfcAdapter) receiveMessageFromDevice(intent) } override fun onPause() { super.onPause() disableForegroundDispatch(this, this.nfcAdapter) } private fun receiveMessageFromDevice(intent: Intent) { val action = intent.action if (NfcAdapter.ACTION_TAG_DISCOVERED == action || NfcAdapter.ACTION_TECH_DISCOVERED == action || NfcAdapter.ACTION_NDEF_DISCOVERED == action) { val ndefMessage = intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES) Log.d(TAG, "Parcelables data: $ndefMessage") if (!ndefMessage.isNullOrEmpty()) { val ndefRecord = (ndefMessage[0] as NdefMessage).records[0] val message = ndefRecord.payload.decodeToString() binding.nfcStatusText.text = "Message from tag NFC: $message" } else { binding.nfcStatusText.text = "No message on tag NFC" } } } private fun enableForegroundDispatch(activity: AppCompatActivity, adapter: NfcAdapter?) { val intent = Intent(this, javaClass) intent.flags = Intent.FLAG_ACTIVITY_SINGLE_TOP val pendingIntent = PendingIntent.getActivity( this, 0, intent, PendingIntent.FLAG_IMMUTABLE ) val filters = arrayOfNulls<IntentFilter>(1) val techList = arrayOf<Array<String>>() filters[0] = IntentFilter() with(filters[0]) { this?.addAction(NfcAdapter.ACTION_TAG_DISCOVERED) this?.addCategory(Intent.CATEGORY_DEFAULT) try { this?.addDataType(MIME_TEXT_PLAIN) } catch (ex: IntentFilter.MalformedMimeTypeException) { throw RuntimeException("Check your MIME type") } } adapter?.enableForegroundDispatch(activity, pendingIntent, filters, techList) } private fun disableForegroundDispatch(activity: AppCompatActivity, adapter: NfcAdapter?) { adapter?.disableForegroundDispatch(activity) } }
Manifest配置
<uses-permission android:name="android.permission.NFC" /> <intent-filter> <action android:name="android.nfc.action.NDEF_DISCOVERED" /> <category android:name="android.intent.category.DEFAULT" /> <data android:mimeType="text/plain" /> </intent-filter>
问题排查及修复方案
Foreground Dispatch过滤器不匹配
enableForegroundDispatch中,ACTION_TAG_DISCOVERED不支持MIME类型过滤,只有ACTION_NDEF_DISCOVERED才会匹配text/plain类型。修改过滤器配置:with(filters[0]) { this?.addAction(NfcAdapter.ACTION_NDEF_DISCOVERED) this?.addCategory(Intent.CATEGORY_DEFAULT) try { this?.addDataType(MIME_TEXT_PLAIN) } catch (ex: IntentFilter.MalformedMimeTypeException) { throw RuntimeException("Check your MIME type") } }未启用设备间传输支持
设备间NFC传输依赖Android Beam(Android 10及以下)或系统内置的分享功能,需补充配置:- 在Manifest中添加NFC功能声明:
<uses-feature android:name="android.hardware.nfc" android:required="true" /> - Android 10及以下需启用Foreground Ndef Push:
Android 11+需使用if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.Q) { nfcAdapter?.enableForegroundNdefPush(this, NdefMessage(NdefRecord.createMime(MIME_TEXT_PLAIN, ByteArray(0)))) }NfcAdapter.setNdefPushMessageCallback替代。
- 在Manifest中添加NFC功能声明:
onResume中错误读取Intent
onResume中的intent是Activity启动Intent,不是NFC触发的Intent,删除该行代码:// receiveMessageFromDevice(intent) 移除这行NDEF文本记录解析不规范
NDEF文本Payload包含状态字节和语言编码,直接解码会出错,替换解析逻辑:private fun parseNdefTextRecord(record: NdefRecord): String { if (record.tnf != NdefRecord.TNF_WELL_KNOWN || !Arrays.equals(record.type, NdefRecord.RTD_TEXT)) { return "" } val payload = record.payload val languageCodeLength = payload[0].toInt() and 0x1F return String(payload, languageCodeLength + 1, payload.size - languageCodeLength - 1, Charsets.UTF_8) }在
receiveMessageFromDevice中调用:val message = parseNdefTextRecord(ndefRecord)Manifest补充设备间传输过滤器
添加TECH_DISCOVERED过滤器确保识别设备连接:<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" />在
res/xml目录创建nfc_tech_filter.xml:<resources> <tech-list> <tech>android.nfc.tech.Ndef</tech> <tech>android.nfc.tech.NdefFormatable</tech> </tech-list> </resources>
内容的提问来源于stack exchange,提问作者Antonio
相关产品推荐
相关产品推荐

