You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

无生命周期类监听网络状态+Room缓存UI不更新问题排查

问题分析与解决方案

核心问题点

  1. 数据获取是一次性的:getmovieList()返回的是普通List,而非可观察的流(Flow/LiveData)。数据库更新后,UI无法收到通知,只有重启应用重新调用该方法才能拿到新数据。
  2. 独立协程导致数据不同步:网络监听逻辑用launchIn(CoroutineScope(Dispatchers.IO))启动了独立协程,getmovieList()会直接跳过这个协程,立即返回当前数据库的旧数据;后续API请求完成更新数据库时,UI已经拿到旧数据,不会自动刷新。
  3. 网络状态判断逻辑错误: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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 22:15:00