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

Android中MutableLiveData第二次无法向Fragment传值的问题排查

问题原因分析

你的问题核心出在LiveData的默认行为上:当你给MutableLiveData设置的新值和当前保存的旧值相等时,LiveData不会触发观察者的回调。比如第一次发送了AudioModeStatus("1"),第二次还是发送同样数据的AudioModeStatus,Fragment就收不到通知。另外,onSuccessSettingResponse里循环两次设置stateLiveData.value,后面的赋值会直接覆盖前面的,导致只有最后一个状态能被Fragment收到,而且如果这个状态和之前的一样,也不会触发回调。

解决方案

方案1:用Event包装类(推荐,不拆分LiveData)

创建一个Event类来包装你的状态,确保每次发送都是新的事件对象,即使内部数据相同,LiveData也会触发回调:

// 通用的Event包装类
open class Event<out T>(private val content: T) {
    var hasBeenHandled = false
        private set

    fun getContentIfNotHandled(): T? {
        return if (hasBeenHandled) {
            null
        } else {
            hasBeenHandled = true
            content
        }
    }

    fun peekContent(): T = content
}

// 在ViewModel里修改LiveData类型
override val stateLiveData: MutableLiveData<Event<CameraR2Status>> = MutableLiveData()

// 发送状态时用Event包装
override fun onSuccessAudioMode(response: CardStatusResponse?) {
    stateLiveData.value = Event(AudioModeStatus(response?.status.toString()))
}

override fun onSuccessUpdatedRecordingStatus(response: CardStatusResponse?) {
    stateLiveData.value = Event(RecordingStatus(response?.status.toString()))
}

override fun onSuccessSettingResponse(response: SettingsResponse?) {
    response?.let {
        it.cmd.forEachIndexed { index, item ->
            if (item == "2007") {
                stateLiveData.value = Event(AudioModeStatus(it.status[index]))
            }
        }
        it.cmd.forEachIndexed { index, item ->
            if (item == "2001") {
                stateLiveData.value = Event(RecordingStatus(it.status[index]))
            }
        }
    }
}

然后在Fragment里处理Event,确保只处理一次:

private fun onRoveR2CameraResponse(event: Event<BaseViewModel.CameraR2Status>) {
    event.getContentIfNotHandled()?.let { state ->
        when (state) {
            is BaseViewModel.CameraR2Status.AudioModeStatus -> {
                r2VideoControlWidget.showAudioStatusIcon(state.data)
            }
            is BaseViewModel.CameraR2Status.RecordingStatus -> {
                showLiveVideoWhenR2Connected()
                when (state.data) {
                    "1" -> r2VideoControlWidget.showPlayStopButton(true)
                    "0" -> r2VideoControlWidget.showPlayStopButton(false)
                }
            }
        }
    }
}

方案2:拆分独立的LiveData

如果觉得包装类麻烦,可以给每个状态单独创建LiveData,这样各个状态的更新互不干扰,也不会出现覆盖问题:

class Rove2LiveVideoViewModelImpl @Inject constructor(
    private val roveR2UseCase: RoveR2UseCase,
    private val roveR2LiveVideoUseCase: RoveR2LiveVideoUseCase,
    private val appPreference: AppPreference
) : Rove2LiveVideoViewModel(), RoveR2UseCase.Callback, RoveR2LiveVideoUseCase.Callback {
    // 拆分独立的LiveData
    val audioModeLiveData: MutableLiveData<String> = MutableLiveData()
    val recordingStatusLiveData: MutableLiveData<String> = MutableLiveData()

    init {
        roveR2UseCase.setCallback(this)
        roveR2LiveVideoUseCase.setCallback(this)
    }

    override fun onSuccessAudioMode(response: CardStatusResponse?) {
        audioModeLiveData.value = response?.status.toString()
    }

    override fun onSuccessUpdatedRecordingStatus(response: CardStatusResponse?) {
        recordingStatusLiveData.value = response?.status.toString()
    }

    override fun onSuccessSettingResponse(response: SettingsResponse?) {
        response?.let {
            it.cmd.forEachIndexed { index, item ->
                if (item == "2007") {
                    audioModeLiveData.value = it.status[index]
                }
                if (item == "2001") {
                    recordingStatusLiveData.value = it.status[index]
                }
            }
        }
    }
}

Fragment里分别观察:

rove2LiveVideoViewModel.apply {
    getR2CameraConnectedOrNot()
    observe(audioModeLiveData) { status ->
        r2VideoControlWidget.showAudioStatusIcon(status)
    }
    observe(recordingStatusLiveData) { status ->
        showLiveVideoWhenR2Connected()
        r2VideoControlWidget.showPlayStopButton(status == "1")
    }
}

额外优化点

onSuccessSettingResponse里原来的两次循环可以合并成一次,避免重复遍历列表:

override fun onSuccessSettingResponse(response: SettingsResponse?) {
    response?.let {
        it.cmd.forEachIndexed { index, item ->
            when(item) {
                "2007" -> audioModeLiveData.value = it.status[index]
                "2001" -> recordingStatusLiveData.value = it.status[index]
            }
        }
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.07 04:25:21