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

RxJava+Room+Retrofit链式调用异常:收藏项逐次减少至消失

问题分析与解决方案

看起来你遇到的核心问题是RxJava流的异步操作不同步,加上共享变量的线程安全风险,导致收藏状态被API返回的未收藏数据覆盖,最终出现收藏项逐渐消失的情况。咱们一步步拆解问题,再给出修正方案:

核心问题点

1. 异步插入导致流程脱节

你的insertKafaniInDb方法内部是创建Observable后直接subscribe,这是一个完全独立的异步流,和上游的API请求流没有关联。也就是说:

  • API数据一返回,getKafaniFromApi()就会立刻发射数据
  • 后续的「更新收藏」「查询全量数据」操作会直接执行,但此时DB插入操作可能还没完成
  • 插入完成后,API返回的未收藏数据会覆盖掉你刚更新的收藏状态,导致最终结果里收藏项消失

2. 共享变量kafaniList存在线程安全隐患

ViewModel里用kafaniList存储之前的收藏列表,在RxJava的多线程流中,这种可变变量可能因为线程调度顺序问题,出现「还没赋值就被使用」或者「值被意外覆盖」的情况。

3. 更新收藏的逻辑效率低且易出错

循环调用updateFavoriteKafana逐个更新,不仅DB操作次数多,还容易因为和插入操作的时序问题导致无效更新。


修正后的代码

第一步:重构Repository,让插入操作与流同步

先修改insertKafaniInDb,让它返回Single,这样就能把插入操作串联到RxJava流中,确保插入完成后再执行后续步骤:

fun insertKafaniInDb(kafani: List<Kafana>): Single<LongArray> {
    return Single.fromCallable { kafanaDao.insertAll(kafani) }
        .subscribeOn(Schedulers.io())
        .doOnSuccess { Timber.d("Inserted ${kafani.size} kafani from API in DB...") }
}

// 同步API请求与DB插入
fun getKafaniFromApi(): Observable<List<Kafana>> {
    return apiService.getKafani()
        .toObservable()
        .flatMap { apiKafani ->
            // 等待插入完成后,再把API数据传递到下游
            insertKafaniInDb(apiKafani).map { apiKafani }
        }
}

第二步:优化ViewModel,去掉共享变量,串联完整流程

用RxJava的操作符传递收藏列表,避免线程安全问题,同时确保每一步操作都在前一步完成后执行:

fun get(): Observable<List<Kafana>> {
    // 1. 先获取当前收藏列表
    return kafanaRepository.getFavoriteKafani()
        .flatMapObservable { favoriteList ->
            // 2. 拉取API数据并插入DB(等待插入完成)
            kafanaRepository.getKafaniFromApi()
                // 3. 批量更新收藏状态(用批量DB操作更高效)
                .flatMap { _ ->
                    Single.fromCallable {
                        // 提取收藏项的名称,批量更新DB
                        val favoriteNames = favoriteList.map { it.name }
                        kafanaDao.setFavoritesByNames(favoriteNames)
                    }.subscribeOn(Schedulers.io())
                }
                // 4. 更新完成后,获取最终的全量数据
                .flatMapObservable {
                    kafanaRepository.getKafaniFromDb()
                }
        }
        .subscribeOn(Schedulers.io())
        .observeOn(AndroidSchedulers.mainThread())
}

第三步:优化Room DAO,添加批量更新方法

为了提高效率,在DAO中新增批量更新收藏状态的方法,避免循环调用单条更新:

@Dao
interface KafanaDao {
    // 确保插入时替换旧数据,这样API数据会覆盖原有记录
    @Insert(onConflict = OnConflictStrategy.REPLACE)
    fun insertAll(kafani: List<Kafana>): LongArray

    // 批量更新收藏状态
    @Query("UPDATE kafana SET is_favorite = 1 WHERE name IN (:names)")
    fun setFavoritesByNames(names: List<String>)

    // ... 其他原有方法
}

为什么这样修改能解决问题?

  1. 全流程同步:现在每一步操作(查询收藏→API请求→插入DB→更新收藏→查询全量)都是按顺序执行的,前一步完成后才会触发后一步,彻底避免了异步操作的时序冲突。
  2. 无共享变量:通过RxJava的操作符把收藏列表直接传递到后续操作中,消除了线程安全隐患。
  3. 高效批量操作:用单条SQL批量更新收藏状态,减少了DB操作次数,同时避免了循环更新带来的错误。

内容的提问来源于stack exchange,提问作者Esteban

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 07:29:51