Android Jetpack Compose实现NFC屏幕读写的异常问题排查
问题分析与修复方案
你的代码存在几个关键问题,导致NFC前台分发(ForegroundDispatch)未生效,系统触发默认扫描器,且日志无法输出:
1. 变量初始化顺序错误
nfcAdapter的初始化依赖activity,但你先声明nfcAdapter再获取activity,会导致未初始化错误或空指针异常,后续代码直接跳过,自然没有日志输出。
2. PendingIntent目标类错误
Intent(activity, javaClass)中的javaClass在Composable函数里指向的是NFCReader这个Composable的类,而非当前Activity类,导致ForegroundDispatch的Intent无法正确分发到Activity,系统 fallback 到默认扫描器。
3. 笔误导致资源未正确释放
onDispose中的activity1是未定义变量,应该改为activity,否则会抛出异常,导致监听器和生命周期观察者无法移除。
4. IntentFilter配置问题
ACTION_NDEF_DISCOVERED需要指定具体的MIME类型或URI才能匹配,否则该过滤器不会生效;同时三个过滤器的顺序不符合NFC优先级(NDEF > TECH > TAG),可能导致匹配逻辑异常。
修正后的完整代码
@Composable fun NFCReader( lifecycleOwner: LifecycleOwner = LocalLifecycleOwner.current ) { val context = LocalContext.current val activity = context.getActivity() as ComponentActivity val nfcAdapter = NfcAdapter.getDefaultAdapter(context) DisposableEffect(lifecycleOwner, nfcAdapter) { // 先判断设备是否支持NFC if (nfcAdapter == null) { Log.d("TAG_", "设备不支持NFC") onDispose {} return@DisposableEffect } val observer = LifecycleEventObserver { _, event -> when (event) { Lifecycle.Event.ON_RESUME -> { Log.d("TAG_", "启动NFC前台分发") val pendingIntent = PendingIntent.getActivity( activity, 0, Intent(activity, activity::class.java).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), PendingIntent.FLAG_MUTABLE ) // 调整IntentFilter顺序,为NDEF指定MIME类型(示例为text/plain,根据你的标签调整) val intentFilters = arrayOf( IntentFilter(NfcAdapter.ACTION_NDEF_DISCOVERED).apply { try { addDataType("text/plain") } catch (e: IntentFilter.MalformedMimeTypeException) { Log.e("TAG_", "MIME类型格式错误", e) } }, IntentFilter(NfcAdapter.ACTION_TECH_DISCOVERED), IntentFilter(NfcAdapter.ACTION_TAG_DISCOVERED) ) // 可传入特定NFC技术列表,匹配指定类型的标签,无需则传null val techLists = arrayOf(arrayOf(Ndef::class.java.name)) runCatching { nfcAdapter.enableForegroundDispatch(activity, pendingIntent, intentFilters, techLists) }.onFailure { Log.e("TAG_", "启动前台分发失败", it) } } Lifecycle.Event.ON_PAUSE -> { Log.d("TAG_", "关闭NFC前台分发") runCatching { nfcAdapter.disableForegroundDispatch(activity) }.onFailure { Log.e("TAG_", "关闭前台分发失败", it) } } else -> {} } } lifecycleOwner.lifecycle.addObserver(observer) val intentListener = Consumer<Intent> { intent -> when (intent.action) { NfcAdapter.ACTION_NDEF_DISCOVERED, NfcAdapter.ACTION_TECH_DISCOVERED, NfcAdapter.ACTION_TAG_DISCOVERED -> { Log.d("TAG_", "捕获到NFC标签,action: ${intent.action}") // 在这里处理标签读写逻辑 } } } activity.addOnNewIntentListener(intentListener) onDispose { activity.removeOnNewIntentListener(intentListener) lifecycleOwner.lifecycle.removeObserver(observer) // 确保退出时强制关闭前台分发 runCatching { nfcAdapter.disableForegroundDispatch(activity) } } } } // 扩展函数:从Context获取对应Activity @Suppress("UNCHECKED_CAST") fun Context.getActivity(): ComponentActivity? { var context = this while (context is ContextWrapper) { if (context is ComponentActivity) { return context } context = context.baseContext } return null }
额外注意事项
- Manifest配置:确保在
AndroidManifest.xml中添加NFC权限和相关配置:<!-- NFC权限 --> <uses-permission android:name="android.permission.NFC" /> <!-- 声明应用使用NFC特性 --> <uses-feature android:name="android.hardware.nfc" android:required="false" /> <!-- 在目标Activity中添加intent-filter(可选,帮助系统识别应用支持NFC) --> <activity android:name=".MainActivity"> <intent-filter> <action android:name="android.nfc.action.NDEF_DISCOVERED" /> <category android:name="android.intent.category.DEFAULT" /> <data android:mimeType="text/plain" /> </intent-filter> </activity> - MIME类型匹配:如果你的NFC标签不是
text/plain类型,需要修改IntentFilter中的addDataType参数,或改为匹配URI(若标签为URI类型)。 - FLAG_MUTABLE:Android 12及以上版本需要使用
PendingIntent.FLAG_MUTABLE或FLAG_IMMUTABLE,此处因需要修改Intent使用MUTABLE。 - 错误捕获:添加
runCatching可以捕获异常并输出日志,方便排查问题。
内容的提问来源于stack exchange,提问作者just_asking
相关产品推荐
相关产品推荐

