Jetpack Compose中viewModelScope阻塞UI,如何使用viewModelScope.launch(Dispatchers.IO)?
解决viewModelScope阻塞UI的正确用法
你的UI阻塞根源是getCountryList被直接在UI线程调用,且CountrySource内部的API请求属于同步阻塞操作。以下是具体修正方案:
1. 重构ViewModel逻辑
把countryGroupList改为MutableStateFlow,同时用viewModelScope.launch(Dispatchers.IO)将耗时操作切换到IO线程执行:
@HiltViewModel class CountryViewModel @Inject constructor() : BaseViewModel() { // 改用MutableStateFlow维护分页数据状态,初始值设为空分页数据 private val _countryGroupList = MutableStateFlow<PagingData<CountryGroup>>(PagingData.empty()) val countryGroupList: StateFlow<PagingData<CountryGroup>> = _countryGroupList private val config = PagingConfig(pageSize = 26, prefetchDistance = 1, initialLoadSize = 26) private var isLoading = false // 防止重复启动请求的标记 fun getCountryList() { if (isLoading) return // 切换到IO线程执行分页流的创建与数据收集 viewModelScope.launch(Dispatchers.IO) { isLoading = true try { val pagingFlow = Pager(config) { CountrySource(api) }.flow // 收集分页流数据,同步到StateFlow pagingFlow.collect { _countryGroupList.value = it } } finally { isLoading = false } } } }
2. 调整UI层的数据收集方式
因为countryGroupList现在是StateFlow,需要先收集状态再转换为分页项:
@Composable fun CountryContent(viewModel: CountryViewModel) { SingleRun { viewModel.getCountryList() } // 先收集StateFlow的最新状态 val pagingData = viewModel.countryGroupList.collectAsState().value val pagingItems = pagingData.collectAsLazyPagingItems() // ... 后续UI渲染逻辑 }
核心原理说明
viewModelScope.launch(Dispatchers.IO)将Pager初始化、CountrySource的耗时API请求转移到IO线程执行,完全避免占用UI线程导致阻塞。MutableStateFlow以非阻塞的方式向UI层推送数据更新,Compose会自动触发重组刷新界面。
内容的提问来源于stack exchange,提问作者gaohomway
相关产品推荐
相关产品推荐

