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

如何在Firebase音频上传协程完成后关闭Fragment?禁用runBlocking

解决方案

方案一:使用StateFlow通知任务完成(推荐,符合MVVM架构)

这种方式让ViewModel通过状态流通知Fragment任务完成状态,避免ViewModel直接依赖导航逻辑,更符合组件解耦原则。

修改ViewModel代码:

import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow

class YourViewModel(private val repository: YourRepository) : ViewModel() {
    private val viewModelScope = ViewModelScope(Dispatchers.Main)
    
    // 用于通知请求添加完成的状态流
    private val _requestAdded = MutableStateFlow<Boolean?>(null)
    val requestAdded: StateFlow<Boolean?> = _requestAdded

    fun addRequest(request: Event){
        viewModelScope.launch {
            try {
                val uri = repository.uploadAudioFile()
                request.audio = uri
                repository.addRequest(request)
                _requestAdded.value = true // 标记任务成功完成
            } catch (e: Exception) {
                _requestAdded.value = false // 标记任务失败,可按需处理
                // 这里可以添加错误日志上报或其他异常处理
            }
        }
    }

    // 重置状态,避免配置变化(如屏幕旋转)后重复触发导航
    fun resetRequestAddedState() {
        _requestAdded.value = null
    }
}

修改Fragment代码:

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)

    // 观察任务完成状态
    viewLifecycleOwner.lifecycleScope.launch {
        viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
            prosbyViewModel.requestAdded.collect { isSuccess ->
                isSuccess?.let {
                    if (it) {
                        // 任务完成,关闭当前Fragment
                        requireActivity().supportFragmentManager.popBackStack()
                    }
                    // 重置状态,防止后续重复触发
                    prosbyViewModel.resetRequestAddedState()
                }
            }
        }
    }

    // 触发添加请求的逻辑(示例:点击按钮时执行)
    yourSubmitButton.setOnClickListener {
        val newEvent = Event(title = title, description = description, date = date)
        prosbyViewModel.addRequest(newEvent)
    }
}

方案二:让addRequest返回Deferred(适合简单场景)

这种方式直接让ViewModel的函数返回异步任务的Deferred对象,Fragment在协程中等待任务完成后执行导航。

修改ViewModel代码:

fun addRequest(request: Event): Deferred<Unit> {
    return viewModelScope.async {
        val uri = repository.uploadAudioFile()
        request.audio = uri
        repository.addRequest(request)
    }
}

修改Fragment代码:

yourSubmitButton.setOnClickListener {
    viewLifecycleOwner.lifecycleScope.launch {
        try {
            val newEvent = Event(title = title, description = description, date = date)
            // 等待异步任务完成
            prosbyViewModel.addRequest(newEvent).await()
            // 任务完成后关闭Fragment
            requireActivity().supportFragmentManager.popBackStack()
        } catch (e: Exception) {
            // 处理上传或添加请求失败的情况,比如显示错误提示
            Toast.makeText(requireContext(), "操作失败", Toast.LENGTH_SHORT).show()
        }
    }
}

为什么不能用runBlocking?

runBlocking会阻塞当前线程(这里是主线程),等待异步任务期间UI会完全卡顿,严重时会触发ANR(应用无响应),绝对不能在主线程中使用这种方式等待耗时任务。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.15 03:55:21