【问题标题】:Loading data from Database + Network (Room + Retrofit + RxJava2)从数据库+网络加载数据(Room + Retrofit + RxJava2)
【发布时间】:2017-11-18 15:20:38
【问题描述】:

我有一个示例 API 请求,它返回用户关注列表的列表。我想在用户加载监视列表屏幕时实现以下流程

  1. 立即从数据库缓存中加载数据。(cacheWatchList)

  2. 在后台发起 RetroFit 网络调用。

    我。 onSuccess返回apiWatchList
    ii. onError返回cacheWatchList

  3. 区分 cacheWatchListapiWatchList

    我。相同 -> 一切都很好,因为数据已经显示给用户什么都不做。

    二。不同 -> 将apiWatchList 保存到本地存储并将apiWatchList 发送到下游。

到目前为止我做了什么?

Watchlist.kt

data class Watchlist(
  val items: List<Repository> = emptyList()
)

LocalStore.kt(Android 房间)

  fun saveUserWatchlist(repositories: List<Repository>): Completable {    
    return Completable.fromCallable {      
      watchlistDao.saveAllUserWatchlist(*repositories.toTypedArray())
    }
  }

RemoteStore.kt(改造 api 调用)

  fun getWatchlist(userId: UUID): Single<Watchlist?> {
    return api.getWatchlist(userId)
  }

DataManager.kt

  fun getWatchlist(userId: UUID): Flowable<List<Repository>?> {
    val localSource: Single<List<Repository>?> =
      localStore.getUserWatchlist()
        .subscribeOn(scheduler.computation)

    val remoteSource: Single<List<Repository>> = remoteStore.getWatchlist(userId)
      .map(Watchlist::items)
      .doOnSuccess { items: List<Repository> ->
        localStore.saveUserWatchlist(items)
          .subscribeOn(scheduler.io)
          .subscribe()
      }
      .onErrorResumeNext { throwable ->
        if (throwable is IOException) {
          return@onErrorResumeNext localStore.getUserWatchlist()
        }
        return@onErrorResumeNext Single.error(throwable)
      }
      .subscribeOn(scheduler.io)

    return Single.concat(localSource, remoteSource)
  }

上述流程的问题是,即使两个数据相同,它也会为下游(演示者)的每个流源调用onNext 两次

我可以在演示者中执行数据差异逻辑并进行相应更新,但我希望DataManager 类为我处理逻辑(CleanArchitecture,SOC)。

我的问题?

  1. 实现上述逻辑的最佳方法是什么?

  2. 我是否泄露了 DataManager 中的内部订阅(参见:doOnSuccess 代码)?当演示者被销毁时,我正在处理外部订阅。

【问题讨论】:

  • 试过google的paging库?
  • @pskink 通过将默认适配器扩展到PagedListAdapter,我们可以解决这个问题。但是再次PagedListAdapter导致表示层发生变化,我希望数据层来处理数据变化。
  • 什么意思?你不能 invalidate() 你的 DataSource 吗?
  • @pskink 太简短了,我不想在表示(UI)层写一些逻辑,改变适配器实现或写 DiffUtils 回调机制。我想让演示者尽可能地愚蠢,希望数据层负责在正确的时间发送正确的数据。
  • 您的演示者只需创建LivePagedListProvider 以及您将在自定义DataSource 中实现的所有逻辑

标签: android retrofit2 rx-java2 android-room


【解决方案1】:
fun getWatchlist(userId: UUID): Observable<List<Repository>?> {
val remoteSource: Single<List<Repository>> = 
remoteStore.getWatchlist(userId)
        .map(Watchlist::items)
        .subscribeOn(scheduler.io)

return localStore.getUserWatchlist()
        .flatMapObservable { listFromLocal: List<Repository> ->
            remoteSource
                    .observeOn(scheduler.computation)
                    .toObservable()
                    .filter { apiWatchList: List<Repository> ->
                        apiWatchList != listFromLocal
                    }
                    .flatMapSingle { apiWatchList ->
                        localSource.saveUserWatchlist(apiWatchList)
                                .andThen(Single.just(apiWatchList))
                    }
                    .startWith(listFromLocal)
        }
}

一步一步解释:

  1. 从 localStore 加载数据
  2. 每次 localStore 发出数据时,使用 flatMapObservable 订阅 remoteSource。
  3. 由于内部 observable 有多个发射(本地的初始数据和来自 remoteSource 的更新数据的新数据)将 Single 转换为 Observable。
  4. 将 remoteSource 中的数据与 localStore 中的数据进行比较,并仅在 newData != localData 的情况下处理数据。
  5. 对于过滤器启动 localSource 后的每个发射以保存数据,并在此操作完成后继续将保存的数据作为 Single。
  6. 根据要求,应在开始时从 localStore 处理远程请求数据,只需在操作链的末尾添加 startWith 即可。

【讨论】:

  • 谢谢,问题解决了。请为代码的返回部分添加一点文字说明,对整个社区都有好处。然后我会将其标记为答案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-02-04
  • 2018-11-05
  • 1970-01-01
  • 2019-07-13
  • 1970-01-01
相关资源
最近更新 更多