如何让ViewModel感知Preference DataStore变化并更新天气数据
实现DataStore变化时自动更新ViewModel的天气状态
我有一个存储地点列表的DataStore,相关代码如下。该列表用于从API获取天气数据,但目前向DataStore添加地点时,ViewModel无法感知变化并更新_weatherState获取新数据,希望解决这个问题。
DataStore相关代码
private val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "location_list") private val dataStoreKey = stringSetPreferencesKey(LOCATION_KEY) suspend fun saveLocation(context: Context, data: String) { val locationSet = getLocations(context).first().toMutableSet() val isAdd = locationSet.add(data) if (isAdd) { val newSet = locationSet.toSet() context.dataStore.edit { preference -> preference[dataStoreKey] = newSet } } } suspend fun getLocations(context: Context): Flow<Set<String>> { val preferences = context.dataStore.data.first() if (preferences[dataStoreKey] != null) { return flowOf(preferences[dataStoreKey]!!) } return flowOf(emptySet()) }
获取天气数据的ViewModel代码
fun getWeatherData(context: Context) { getCurrentLocation(context) {currentLocation -> val weatherData = mutableListOf<Deferred<WeatherDataDaily>>() _weatherState.value.isLoading = true viewModelScope.launch { citiesRepository.getLocations(context).collect { val locationList = it.toMutableList() locationList.add(0, currentLocation.getLocationValue()) for (value in locationList) { val data = async { repository.getWeatherData(value) } weatherData.add(data) } _weatherState.value = WeatherState( weatherData.awaitAll(), isInitialize = true ) } } _weatherState.value.isLoading = false Log.d(TAG, "getWeatherData: ") } }
解决方案
1. 修复DataStore的getLocations方法,使其持续监听数据变化
原来的getLocations使用first()只获取一次DataStore数据,修改后返回能持续监听的Flow,每次DataStore数据变更都会触发回调:
suspend fun getLocations(context: Context): Flow<Set<String>> { return context.dataStore.data .map { preferences -> preferences[dataStoreKey] ?: emptySet() } }
2. 重构ViewModel的getWeatherData方法,正确响应数据变化
调整加载状态的时机,简化并发请求逻辑,确保每次DataStore更新都触发天气数据刷新:
fun getWeatherData(context: Context) { getCurrentLocation(context) { currentLocation -> viewModelScope.launch { citiesRepository.getLocations(context).collect { locationSet -> // 开始加载时更新状态 _weatherState.value = _weatherState.value.copy(isLoading = true) // 构造包含当前位置的地点列表 val locationList = locationSet.toMutableList().apply { add(0, currentLocation.getLocationValue()) } // 并发获取所有地点的天气数据 val weatherData = locationList.map { location -> async { repository.getWeatherData(location) } }.awaitAll() // 更新状态为加载完成并设置新数据 _weatherState.value = WeatherState( weatherData = weatherData, isInitialize = true, isLoading = false ) } } } }
关键说明
- 修复后的
getLocations会持续监听DataStore的变化,每次添加/删除地点时都会触发collect块执行。 - 加载状态的更新被移到collect块内部,避免了之前提前设置为false的问题,确保用户能看到正确的加载提示。
- 使用
map+awaitAll简化了异步请求的代码,避免了可变列表的潜在问题,逻辑更清晰。
内容的提问来源于stack exchange,提问作者HaBuiDuc
相关产品推荐
相关产品推荐

