以 MVI 模式提供或发送频道的正确位置

Right place to offer or send Channel in MVI pattern

我提前在recycleView中加载了数据。为了做到这一点,我在 Activity 的 onCreate() 中有以下代码:

override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        setupUI()
        setupViewModel()
        observeViewModel()
        if (savedInstanceState == null) {
            mainViewModel.userIntent.offer(MainIntent.FetchUser)
        }
    }

如您所见,我 offer() 当 savedInstanceState 为 null 时,问题是当我们有进程死亡时(您可以通过激活开发人员选项中的不保留活动来简单地创建它),不会重新加载数据触发。

另一种选择是在 ViewModel 的 init 块中使用它,但问题是我想进行下面的单元测试,我可以验证所有三种状态:

    @Test
    fun givenServerResponse200_whenFetch_shouldReturnSuccess() {
        runBlockingTest {
            `when`(apiService.getUsers()).thenReturn(emptyList())
            val apiHelper = ApiHelperImpl(apiService)
            val repository = MainRepository(apiHelper)
            val viewModel = MainViewModel(repository)
            viewModel.state.asLiveData().observeForever(observer)
            viewModel.userIntent.send(MainIntent.FetchUser)
        }
        verify(observer, times(3)).onChanged(captor.capture())
        verify(observer).onChanged(MainState.Idle)
        verify(observer).onChanged(MainState.Loading)
        verify(observer).onChanged(MainState.Users(emptyList()))
    }

如果我在 ViewModel 初始化后立即使用 init 块选项,sendoffer 将被调用,而 observeForever 在上述单元测试中未用于 LiveData。

这是我的 ViewModel class :

class MainViewModel(
    private val repository: MainRepository
) : ViewModel() {

    val userIntent = Channel<MainIntent>(Channel.UNLIMITED)
    private val _state = MutableStateFlow<MainState>(MainState.Idle)
    val state: StateFlow<MainState>
        get() = _state

    init {
        handleIntent()
    }

    private fun handleIntent() {
        viewModelScope.launch {
            userIntent.consumeAsFlow().collect {
                when (it) {
                    is MainIntent.FetchUser -> fetchUser()
                }
            }
        }
    }

    private fun fetchUser() {
        viewModelScope.launch {
            _state.value = MainState.Loading
            _state.value = try {
                MainState.Users(repository.getUsers())
            } catch (e: Exception) {
                MainState.Error(e.localizedMessage)
            }
        }
    }
}

以上情况的解决方案是什么?

我找到的唯一解决方案是将 fetchUser 方法和另一个 _state 作为 MutableStateFlow 移动到 Repository 层,然后 observeForever 它在 Repository 中用于本地单元测试,因此我可以在 ViewModel 的初始化块中发送或提供 userIntent

我将在 ViewModel 中有以下 _state

val userIntent = Channel<MainIntent>(Channel.UNLIMITED)
    private val _state = repository.state
    val state: StateFlow<MainState>
        get() = _state