双Fragment赛事数据展示异常:已结束赛事无法同步至Recent Races列表
解决方案:同步已结束赛事到Recent Races列表
看起来你的核心问题是数据更新时,Recent Races列表没有触发重新过滤和刷新,而Upcoming Races能正常工作,大概率是数据流的触发逻辑或订阅方式出了问题。以下是分步排查和修复方案:
1. 先优化低效且有风险的日期过滤逻辑
你当前的过滤代码存在重复创建格式化器、冗余日期转换的问题,而且SimpleDateFormat不是线程安全的,可能导致解析异常。先把这部分重构得更简洁可靠:
// 把日期格式化器提到方法外,DateTimeFormatter是线程安全的,可复用 private val dateFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd") private fun getDetails() { getRaceDetailsUseCase().onEach { result -> when (result) { is Resource.Success -> { val currentDate = LocalDate.now() // 直接获取当前本地日期,跳过字符串转换 val filteredRecentRaces = result.data.filter { race -> // 用runCatching处理日期解析失败的情况,避免崩溃 runCatching { val raceDate = LocalDate.parse(race.date, dateFormatter) currentDate >= raceDate // 筛选已结束(含当天)的赛事 }.getOrDefault(false) } _state1.value = Resource.Success(filteredRecentRaces) } is Resource.Error -> { _state1.value = Resource.Error("Failed to load recent races") } is Resource.Loading -> { _state1.value = Resource.Loading(true) } } }.launchIn(viewModelScope) }
2. 确保数据流能同步更新所有订阅者
你的RaceDetailsUseCase返回的是冷流(每次调用invoke()才会发起一次API请求),这意味着:当API数据更新时,Recent Races的ViewModel不会自动收到新数据,除非你主动重新调用getDetails()。而Upcoming Races可能因为某种逻辑(比如定时刷新或用户交互)重新触发了请求,所以能正常更新。
要解决这个问题,需要把Repository的数据流改成热流(比如StateFlow),让所有订阅者(Recent和Upcoming的ViewModel)能实时收到数据更新:
第一步:修改Repository,维护一个可更新的StateFlow
class RaceResultsRepository { // 用MutableStateFlow维护全局赛事数据状态 private val _raceData = MutableStateFlow<Resource<List<RaceDomain>>>(Resource.Loading(true)) val raceData: StateFlow<Resource<List<RaceDomain>>> = _raceData // 主动刷新数据的方法,调用API后更新StateFlow suspend fun refreshRaceResults() { try { _raceData.value = Resource.Loading(true) val newRaceData = // 调用你的API接口获取最新赛事数据 _raceData.value = Resource.Success(newRaceData) } catch (e: HttpException) { _raceData.value = Resource.Error("Network error occurred") } catch (e: IOException) { _raceData.value = Resource.Error("Failed to connect to server") } } // 对外提供订阅数据流的方法 fun getRaceResultsFlow(): StateFlow<Resource<List<RaceDomain>>> { // 如果还没加载过数据,自动触发一次刷新 if (_raceData.value is Resource.Loading) { // 注意:这里需要用CoroutineScope,可在Repository构造时注入或用GlobalScope(不推荐,最好用DI提供的scope) CoroutineScope(Dispatchers.IO).launch { refreshRaceResults() } } return raceData } }
第二步:修改UseCase,返回热流
class RaceDetailsUseCase @Inject constructor( private val repository: RaceResultsRepository ) { operator fun invoke(): StateFlow<Resource<List<RaceDomain>>> { // 直接返回Repository的StateFlow,不再每次创建新的flow return repository.getRaceResultsFlow() } }
3. 确保Fragment正确观察ViewModel状态
最后检查Recent Races的Fragment是否用viewLifecycleOwner观察状态,避免内存泄漏或数据不更新:
// 在RecentRacesFragment的onViewCreated方法中 recentRacesViewModel.state1.observe(viewLifecycleOwner) { resource -> when (resource) { is Resource.Success -> { // 更新RecyclerView适配器数据 yourAdapter.submitList(resource.data) } is Resource.Error -> { // 显示错误提示,比如Toast或空状态布局 Toast.makeText(requireContext(), resource.message, Toast.LENGTH_SHORT).show() } is Resource.Loading -> { // 显示加载动画或骨架屏 } } }
额外验证点
- 确认Upcoming Races的过滤条件是
currentDate < raceDate,和Recent的currentDate >= raceDate形成互补,确保所有赛事都会被分到其中一个列表(排除日期解析失败的情况)。 - 当API数据更新时,调用
repository.refreshRaceResults(),此时两个ViewModel都会收到新数据,自动重新过滤并更新各自的列表。
内容的提问来源于stack exchange,提问作者collapseCorpse
相关产品推荐
相关产品推荐

