如何在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
相关产品推荐
相关产品推荐

