当收集到的 StateFlow 未更改 kotlin 时,Api 没有响应

No response from Api when collected StateFlow does not change kotlin

在我的片段中,我有一个 RecyclerView,它显示我在选项菜单中输入的查询的结果。这是一个 API,我从中收到电视节目列表。

查询至少需要len为3的字符串。当它为 1 或 2 时,适配器被清除。

override fun onQueryTextChange(newText: String?): Boolean {
if (newText != null && newText.length > 2) {
 if (!newText.isNullOrBlank() && newText.length > 2)
 viewModel.searchMovies(newText)
 } 
else {
adapter.setMoviesList(emptyList())
     }
return true
}

但是,我在输入例如“猫”两次。我收到了一份有猫的节目清单。从选项菜单中删除查询并再次点击后,适配器为空。并且没有相同的搜索。对我来说->因为流量值没变。

在 ViewModel 中我有:

    private val _moviesStateFlow = MutableStateFlow<List<TvMazeShowResponse>>(emptyList())
    val moviesStateFlow = _moviesStateFlow as StateFlow<List<TvMazeShowResponse>>

    fun searchMovies(query: String) {
        viewModelScope.launch {
            val response = api.getApiResponse(query)
            _moviesStateFlow.emit(response)
        }
    }

而这个 StateFlow 我收集在片段中。

        lifecycleScope.launch {
            viewModel.moviesStateFlow.collect {
                adapter.setMoviesList(it)
            }
        }

为了解决这个问题,我在 VM 中添加了另一个功能

    fun clearFlow() {
        viewModelScope.launch {
            _moviesStateFlow.emit(emptyList())
        }
    }

现在我在 onQueryTextChange 的片段中添加了 else。

else {
adapter.setMoviesList(emptyList())
viewModel.clearFlow()
}

现在它按预期工作了。但是有没有更好的方法来实现它?

为了让您的代码不那么复杂,请避免在您的 UI 类 (Fragment/Activity/Adapter) 中进行逻辑处理,并让您的 ViewModel 提供唯一的真实来源。

override fun onQueryTextChange(newText: String?): Boolean {
    viewModel.searchMovies(newText.orEmpty())
    return true
}

// In ViewModel
fun searchMovies(query: String) {
    val trimmedQuery = query.trim()
    viewModelScope.launch {
        val response = if (trimmedQuery.length <= 2) emptyList() else api.getApiResponse(trimmedQuery)
        _moviesStateFlow.emit(response)
    }
}

为了避免 运行 在用户快速输入时出现多个过时查询,我建议在开始新搜索时取消之前的搜索。

private val searchJob? = null

fun searchMovies(query: String) {
    val trimmedQuery = query.trim()
    searchJob?.cancel()
    searchJob = viewModelScope.launch {
        val response = if (trimmedQuery.length <= 2) emptyList() else api.getApiResponse(trimmedQuery)
        _moviesStateFlow.emit(response)
    }
}