You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 11:29:52