Jetpack Compose中如何等待Retrofit调用完成后执行后续函数?
问题
在Jetpack Compose应用中使用Retrofit从API获取数据时,遇到异步顺序问题:调用getLocation函数后会立刻执行viewModel.updateFlightSearch,但此时viewModel.location.value仍为null——因为Retrofit的异步请求还未完成。需要等待Retrofit请求完成、location变量更新后,再执行updateFlightSearch。
现有代码如下:
获取位置的函数
fun getLocation(name: String) = viewModelScope.launch(Dispatchers.IO) { _loading.value = true val response = repository.getLocation(name) when (response) { is Resource.Success -> { _location.value = response.data!!.first() _loading.value = false } is Resource.Loading -> { _loading.value = true } is Resource.Error -> { _error.value = response.message.toString() Log.d("ErrorLocation", _error.value) _loading.value = false } } }
更新航班搜索数据的函数
fun updateFlightSearch( cityDep: String = "", cityArr: String = "", date: String = "", pass: Int = 0 ) { if (cityDep != "") _flightSearch.value.locationDeparture = cityDep if (cityArr != "") _flightSearch.value.locationArrival = cityArr if (date != "") _flightSearch.value.departureTime = date if (pass != 0) _flightSearch.value.passengers = pass }
点击按钮后的调用逻辑
MyDialog( openDialog = viewModel.isDialogOpen, param = viewModel.selectedButtonName.value, ) { city -> when (viewModel.selectedButtonIndex.value) { 0 -> { viewModel.getLocation(city) viewModel.updateFlightSearch(viewModel.location.value.cityCode.toString()) }
解决方案
方法一:将getLocation改为挂起函数,顺序执行
把getLocation改成挂起函数,这样在协程中调用时会等待请求完成,再执行后续代码,从根源上解决异步顺序问题。
修改getLocation:
suspend fun getLocation(name: String) { _loading.value = true val response = repository.getLocation(name) when (response) { is Resource.Success -> { _location.value = response.data!!.first() _loading.value = false } is Resource.Loading -> { _loading.value = true } is Resource.Error -> { _error.value = response.message.toString() Log.d("ErrorLocation", _error.value) _loading.value = false } } }
在点击回调中启动协程,顺序调用两个函数:
MyDialog( openDialog = viewModel.isDialogOpen, param = viewModel.selectedButtonName.value, ) { city -> when (viewModel.selectedButtonIndex.value) { 0 -> { viewModel.viewModelScope.launch { viewModel.getLocation(city) // 用let处理location可能为null的情况,避免空指针 viewModel.location.value?.let { location -> viewModel.updateFlightSearch(cityDep = location.cityCode.toString()) } } } } }
方法二:在getLocation成功分支直接调用updateFlightSearch
如果updateFlightSearch的调用逻辑和getLocation的成功结果强绑定,可以直接在请求成功后触发更新,同时通过参数标记是更新出发地还是目的地:
修改getLocation并添加更新类型枚举:
// 定义枚举标记要更新的位置类型 enum class UpdateType { DEPARTURE, ARRIVAL } fun getLocation(name: String, updateType: UpdateType) = viewModelScope.launch(Dispatchers.IO) { _loading.value = true val response = repository.getLocation(name) when (response) { is Resource.Success -> { val location = response.data!!.first() _location.value = location _loading.value = false // 请求成功后直接更新航班搜索数据 when(updateType) { UpdateType.DEPARTURE -> updateFlightSearch(cityDep = location.cityCode.toString()) UpdateType.ARRIVAL -> updateFlightSearch(cityArr = location.cityCode.toString()) } } is Resource.Loading -> { _loading.value = true } is Resource.Error -> { _error.value = response.message.toString() Log.d("ErrorLocation", _error.value) _loading.value = false } } }
点击回调中调用方式改为:
MyDialog( openDialog = viewModel.isDialogOpen, param = viewModel.selectedButtonName.value, ) { city -> when (viewModel.selectedButtonIndex.value) { 0 -> { viewModel.getLocation(city, UpdateType.DEPARTURE) } } }
方法三:监听location状态变化自动触发更新
如果需要响应location的每次变化来同步航班搜索数据,可以在Compose层用LaunchedEffect监听location的变化:
// 在Compose界面中添加 LaunchedEffect(viewModel.location.value) { viewModel.location.value?.let { location -> // 仅在选中出发地按钮时更新 if(viewModel.selectedButtonIndex.value == 0) { viewModel.updateFlightSearch(cityDep = location.cityCode.toString()) } } }
这种方式适合需要实时响应location变化的场景,但要注意添加判断条件,避免不必要的更新触发。
内容的提问来源于stack exchange,提问作者deja
相关产品推荐
相关产品推荐

