如何在ViewModel初始化时正确发送事件以在Activity启动后打开相机?
问题:Activity创建后通过ViewModel初始化事件启动相机失败,添加延迟才生效
需求是Activity创建完成后打开相机界面,但在ViewModel初始化函数中发送UiEvent.TakePhoto事件时,相机并未启动。仅在ViewModel初始化中添加delay(1000)后,相机才能正常启动。
相关代码
ViewModel初始化函数:
init{ viewModelScope.launch { _eventFlow.emit(UiEvent.TakePhoto) } }
Activity中触发相机的代码:
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) lifecycleScope.launch { lifecycle.repeatOnLifecycle(Lifecycle.State.RESUMED) { viewModel.eventFlow.collectLatest { event -> when(event){ is PhotoViewModel.UiEvent.TakePhoto -> takePhoto() } } } } } private fun takePhoto() { val cameraIntent = Intent(MediaStore.ACTION_IMAGE_CAPTURE) resultLauncher.launch(cameraIntent) }
原因分析
问题核心是事件发射时机早于监听时机:ViewModel初始化时,Activity的lifecycle.repeatOnLifecycle(Lifecycle.State.RESUMED)块还未执行,collectLatest尚未启动监听eventFlow,导致TakePhoto事件直接丢失,没有被接收处理。添加delay(1000)只是靠延迟让Activity完成初始化、启动监听后再发射事件,属于临时 workaround,并非规范方案。
规范解决方案
方案1:在Activity就绪后通知ViewModel发射事件
不在ViewModel初始化时自动发射事件,而是等Activity生命周期就绪后,主动触发ViewModel的事件发射逻辑:
ViewModel修改:
fun triggerTakePhoto() { viewModelScope.launch { _eventFlow.emit(UiEvent.TakePhoto) } }
Activity修改:
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) lifecycleScope.launch { lifecycle.repeatOnLifecycle(Lifecycle.State.RESUMED) { // 先启动事件监听 viewModel.eventFlow.collectLatest { event -> when(event){ is PhotoViewModel.UiEvent.TakePhoto -> takePhoto() } } // 监听启动后触发相机事件 viewModel.triggerTakePhoto() } } }
方案2:使用SharedFlow保留事件
将eventFlow改为MutableSharedFlow并设置replay=1,它会保留最新的1个事件,后续启动的监听也能接收到之前发射的事件:
ViewModel修改:
private val _eventFlow = MutableSharedFlow<UiEvent>(replay = 1) val eventFlow = _eventFlow.asSharedFlow() init{ viewModelScope.launch { _eventFlow.emit(UiEvent.TakePhoto) } }
这种方式适合需要确保事件不丢失的场景,即使Activity启动监听较晚,也能获取到ViewModel初始化时发射的事件。
内容的提问来源于stack exchange,提问作者GrimPloughman
相关产品推荐
相关产品推荐

