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

Android单Activity应用中NFC用例与Activity解耦方案咨询

单Activity架构下NFC用例与Activity解耦方案

你的中间类方案的问题

直接传入InBetweenClass和让NFC用例持有Activity引用本质上没有区别——中间类依然持有Activity的强引用,如果NFC用例的生命周期超过Activity(比如被全局单例持有),还是会导致Activity无法被GC回收,引发内存泄漏。这种包装只是增加了一层调用,并没有真正解耦。

推荐的解耦方案

方案一:ViewModel + LiveData(适配Jetpack单Activity架构)

利用ViewModel的生命周期特性(与Activity/Fragment绑定但不持有其引用),通过LiveData发送事件,让Activity自行处理NFC前台调度的启动与停止,NFC用例的逻辑完全封装在ViewModel中,无需直接接触Activity。

代码示例:

// NFC逻辑ViewModel
class NfcViewModel : ViewModel() {
    private val _nfcAction = MutableLiveData<NfcAction>()
    val nfcAction: LiveData<NfcAction> = _nfcAction

    // 启动NFC前台调度
    fun startForegroundDispatch() {
        _nfcAction.value = NfcAction.Start
    }

    // 停止NFC前台调度
    fun stopForegroundDispatch() {
        _nfcAction.value = NfcAction.Stop
    }

    // 处理NFC扫描结果
    fun handleNfcIntent(intent: Intent) {
        // 这里编写你的NFC标签解析、位置验证逻辑
        // 扫描完成后自动停止调度
        stopForegroundDispatch()
    }

    sealed class NfcAction {
        object Start : NfcAction()
        object Stop : NfcAction()
    }
}

// 单Activity实现
class MainActivity : AppCompatActivity() {
    private lateinit var nfcAdapter: NfcAdapter
    private val nfcViewModel by viewModels<NfcViewModel>()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        nfcAdapter = NfcAdapter.getDefaultAdapter(this) ?: run {
            // 处理设备不支持NFC的情况
            return
        }
        observeNfcActions()
    }

    private fun observeNfcActions() {
        nfcViewModel.nfcAction.observe(this) { action ->
            when (action) {
                NfcViewModel.NfcAction.Start -> enableNfcForegroundDispatch()
                NfcViewModel.NfcAction.Stop -> disableNfcForegroundDispatch()
            }
        }
    }

    private fun enableNfcForegroundDispatch() {
        val intent = Intent(this, javaClass).apply {
            flags = Intent.FLAG_ACTIVITY_SINGLE_TOP
        }
        val pendingIntent = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
            PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_MUTABLE)
        } else {
            PendingIntent.getActivity(this, 0, intent, 0)
        }
        nfcAdapter.enableForegroundDispatch(this, pendingIntent, null, null)
    }

    private fun disableNfcForegroundDispatch() {
        try {
            nfcAdapter.disableForegroundDispatch(this)
        } catch (e: Exception) {
            // 捕获异常,避免调度未启动时调用报错
        }
    }

    override fun onNewIntent(intent: Intent?) {
        super.onNewIntent(intent)
        intent?.takeIf { it.action == NfcAdapter.ACTION_TAG_DISCOVERED }?.let {
            nfcViewModel.handleNfcIntent(it)
        }
    }

    // 示例:在需要启动NFC扫描的地方调用
    private fun startScan() {
        nfcViewModel.startForegroundDispatch()
    }
}

方案二:接口抽象 + 弱引用

定义一个抽象调度接口,让Activity实现该接口,NFC用例持有接口的弱引用,既保证能调用调度方法,又不会因强引用导致Activity泄漏。

代码示例:

// 抽象NFC调度接口
interface NfcDispatcher {
    fun startForegroundDispatch()
    fun stopForegroundDispatch()
}

// NFC用例类
class NfcUseCase(private val dispatcherRef: WeakReference<NfcDispatcher>) {
    fun startScan() {
        dispatcherRef.get()?.startForegroundDispatch()
    }

    private fun stopScan() {
        dispatcherRef.get()?.stopForegroundDispatch()
    }

    fun handleNfcIntent(intent: Intent) {
        // 处理NFC扫描逻辑
        // 扫描完成后自动停止
        stopScan()
    }
}

// Activity实现接口
class MainActivity : AppCompatActivity(), NfcDispatcher {
    private lateinit var nfcAdapter: NfcAdapter
    private lateinit var nfcUseCase: NfcUseCase

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        nfcAdapter = NfcAdapter.getDefaultAdapter(this) ?: run {
            // 处理不支持NFC的情况
            return
        }
        nfcUseCase = NfcUseCase(WeakReference(this))
    }

    override fun startForegroundDispatch() {
        val intent = Intent(this, javaClass).apply {
            flags = Intent.FLAG_ACTIVITY_SINGLE_TOP
        }
        val pendingIntent = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
            PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_MUTABLE)
        } else {
            PendingIntent.getActivity(this, 0, intent, 0)
        }
        nfcAdapter.enableForegroundDispatch(this, pendingIntent, null, null)
    }

    override fun stopForegroundDispatch() {
        try {
            nfcAdapter.disableForegroundDispatch(this)
        } catch (e: Exception) {
            // 处理异常
        }
    }

    override fun onNewIntent(intent: Intent?) {
        super.onNewIntent(intent)
        intent?.takeIf { it.action == NfcAdapter.ACTION_TAG_DISCOVERED }?.let {
            nfcUseCase.handleNfcIntent(it)
        }
    }
}

方案对比

  • ViewModel+LiveData:更贴合Jetpack生态,自动处理生命周期,无需手动管理弱引用,适合单Activity架构的现代Android应用。
  • 接口+弱引用:更轻量化,不依赖Jetpack组件,适合传统架构或对依赖有严格限制的场景,但需要手动处理弱引用的空安全。

内容的提问来源于stack exchange,提问作者Emanuel

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 05:34:52