Android Kotlin:Github API重复加载失败,ResultViewModel.Loading始终为false
核心问题原因
1. getUser方法加载状态发送时机错误
在getUser的flow逻辑中,先执行了同步网络请求repository.getDetailUserFromGithub(username),之后才设置Loading(true)。这意味着网络请求完成后才通知UI“开始加载”,紧接着就会发送Success状态,Loading(true)会被快速覆盖。后续再次调用时,网络请求因缓存或速度快,这个过程更短,最终仅能捕捉到onCompletion发送的Loading(false)。
2. getFollowingUser存在递归调用错误
getFollowingUser方法的flow块中,错误调用了自身方法getFollowingUser(username),而非仓库层的repository.getFollowingUserFromGithub(username),导致无限递归,请求无法正常完成。
3. Activity中重复调用getUser
DetailUserActivity的onCreate里连续两次调用viewModel.getUser(username),既浪费资源,也可能导致请求结果被覆盖。
4. LiveData状态覆盖问题
Loading(true)被后续状态快速覆盖,再次进入页面时,LiveData可能保留之前的Loading(false)状态,新的加载状态无法正确触发UI更新。
修复方案
1. 修正getUser的加载状态时机
将Loading(true)移到onStart操作符中,确保请求开始前通知UI:
fun getUser(username: String) { viewModelScope.launch { flow { val response = repository.getDetailUserFromGithub(username) emit(response) }.onStart { resultDetailUser.value = ResultViewModel.Loading(true) }.onCompletion { resultDetailUser.value = ResultViewModel.Loading(false) }.catch { Log.e("Error ", it.message.toString()) it.printStackTrace() resultDetailUser.value = ResultViewModel.Error(it) }.collect { resultDetailUser.value = ResultViewModel.Success(it) } } }
2. 修复getFollowingUser的递归错误
改为调用仓库层方法:
fun getFollowingUser(username: String) { viewModelScope.launch { flow { val response = repository.getFollowingUserFromGithub(username) emit(response) }.onStart { resultFollowingUser.value = ResultViewModel.Loading(true) }.onCompletion { resultFollowingUser.value = ResultViewModel.Loading(false) }.catch { Log.e("Error ", it.message.toString()) it.printStackTrace() resultFollowingUser.value = ResultViewModel.Error(it) }.collect { resultFollowingUser.value = ResultViewModel.Success(it) } } }
3. 移除重复的getUser调用
删除DetailUserActivity中resultDetailUser.observe之后的viewModel.getUser(username),仅保留一次调用(放在observe注册完成后)。
4. 页面进入时强制刷新(可选)
在DetailUserActivity的onResume中调用viewModel.getUser(username),确保每次进入页面都触发数据刷新,此时UI会正确收到Loading(true)通知。
额外优化建议
- 给
ResultViewModel添加泛型参数,避免类型转换:
sealed class ResultViewModel<out T> { data class Success<out T>(val data: T) : ResultViewModel<T>() data class Error(val exception: Throwable) : ResultViewModel<Nothing>() data class Loading(val isLoading: Boolean) : ResultViewModel<Nothing>() }
- 仓库层网络请求使用
suspend挂起函数,配合Retrofit Coroutine支持,确保异步执行。 - 给LiveData添加
distinctUntilChanged(),避免重复状态触发UI更新。
内容的提问来源于stack exchange,提问作者Dzy

