无生命周期类监听网络状态+Room缓存UI不更新问题排查
问题分析与解决方案
核心问题点
- 数据获取是一次性的:
getmovieList()返回的是普通List,而非可观察的流(Flow/LiveData)。数据库更新后,UI无法收到通知,只有重启应用重新调用该方法才能拿到新数据。 - 独立协程导致数据不同步:网络监听逻辑用
launchIn(CoroutineScope(Dispatchers.IO))启动了独立协程,getmovieList()会直接跳过这个协程,立即返回当前数据库的旧数据;后续API请求完成更新数据库时,UI已经拿到旧数据,不会自动刷新。 - 网络状态判断逻辑错误:
Status.Losing是网络正在断开的状态,此时不应该发起API请求,会增加无效请求的概率。
修正后的代码实现
1. 调整Dao层,返回Flow类型
Room的Flow会自动监听数据库变化,数据更新时自动发射新数据:
@Dao interface movieDao { @Query("SELECT * FROM movies") fun getAll(): Flow<List<movieEntity>> // 改为返回Flow @Insert(onConflict = OnConflictStrategy.REPLACE) suspend fun insertmovieListWithTitles(movies: List<movieEntity>) }
2. 优化NetworkObserver
指定Flow运行线程,简化回调发送逻辑:
enum class Status { Available, Lost, Losing, Unavailable } class NetworkObserver(context: Context) { private val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager fun observe(): Flow<Status> { return callbackFlow { val callback = object : ConnectivityManager.NetworkCallback() { override fun onAvailable(network: Network) { trySend(Status.Available) } override fun onLosing(network: Network, maxMsToLive: Int) { trySend(Status.Losing) } override fun onLost(network: Network) { trySend(Status.Lost) } override fun onUnavailable() { trySend(Status.Unavailable) } } connectivityManager.registerDefaultNetworkCallback(callback) awaitClose { connectivityManager.unregisterNetworkCallback(callback) } } .distinctUntilChanged() .flowOn(Dispatchers.IO) // 指定Flow运行在IO线程 } }
3. 重构Repository逻辑
分离网络监听与数据获取逻辑,让数据获取暴露可观察的Flow:
class movieRepositoryImpl( private val movieService: movieService, private val movieDao: movieDao, private val context: Context ) : movieRepository { private val networkObserver = NetworkObserver(context.applicationContext) private val scope = CoroutineScope(Dispatchers.IO + SupervisorJob()) // 初始化时自动监听网络状态,同步数据 init { observeNetworkAndSyncData() } private fun observeNetworkAndSyncData() { networkObserver.observe() .onEach { status -> // 仅在网络可用时拉取API数据 if (status == Status.Available) { syncDataFromApi() } } .launchIn(scope) } private suspend fun syncDataFromApi() { val response = movieService.getmovieList() if (response.isSuccessful) { response.body()?.let { dto -> val entities = tomovieEntityList(dto.movieListData) movieDao.insertmovieListWithTitles(entities) } } } // 暴露数据库的Flow,自动响应数据变化 override fun getmovieList(): Flow<List<movieModel>> { return movieDao.getAll() .map { entities -> movieModel.asmovieModelList(entities).ifEmpty { emptyList() } } .flowOn(Dispatchers.IO) } }
4. ViewModel层适配
将Flow转为LiveData供UI观察:
class MovieViewModel( private val repository: movieRepository ) : ViewModel() { val movieList: LiveData<List<movieModel>> = repository.getmovieList() .asLiveData(viewModelScope.coroutineContext) }
5. UI层观察数据
viewModel.movieList.observe(this) { movies -> // 直接更新UI,数据库变化时会自动触发回调 }
关键修改说明
- 用Flow实现实时监听:Room的Flow会自动感知数据库变更,数据更新时自动推送新列表到UI。
- 分离网络与数据逻辑:网络监听在仓库初始化时启动,自动同步数据,避免
getmovieList()返回旧数据后无法更新UI。 - 修正网络状态判断:仅在网络可用时发起API请求,减少无效操作。
- 使用SupervisorJob:确保单个协程失败不会影响其他协程运行,提升稳定性。
内容的提问来源于stack exchange,提问作者Prexi
相关产品推荐
相关产品推荐

