使用 SQLBrite + Retrofit 刷新数据

Refreshing data using SQLBrite + Retrofit

这是我的用例:

我正在开发一个应用程序,它通过 REST API 与服务器通信并将接收到的数据存储在 SQLite 数据库中(它把它用作某种缓存)。

当用户打开屏幕时,必须发生以下情况:

  1. 数据是从数据库加载的(如果可用)。
  2. 应用调用API刷新数据。
  3. API 调用的结果保存到数据库中。
  4. 拦截到数据变化通知后,从数据库中重新加载数据。

这与 here 中的案例非常相似,但略有不同。

因为我使用的是 SQLBrite,DB observables 不会终止(因为那里注册了一个 ContentObserver,它将新数据推送到流中),所以 concatmerge, 等等是行不通的。

目前,我已经使用以下方法解决了这个问题:

Observable.create(subscriber -> {
    dbObservable.subscribe(subscriber);
    apiObservable
        .subscribeOn(Schedulers.io())
        .observeOn(Schedulers.io())
        .subscribe(
            (data) -> {
                try {
                    persistData(data);
                } catch (Throwable t) {
                    Exceptions.throwOrReport(t, subscriber);
                }
            },

            (throwable) -> {
                Exceptions.throwOrReport(throwable, subscriber);
            })
})

看起来它工作正常,但它看起来并不优雅 "correct"。

你能建议或指点一个资源来解释什么是处理这种情况的最佳方法吗?

如果您稍微改变一下思维方式,您的问题的解决方案实际上非常简单明了。我正在使用完全相同的数据交互 (Retrofit + Sqlbrite),这个解决方案非常有效。

您需要做的是使用两个单独的可观察订阅,它们处理完全不同的进程。

  1. Database -> View: 这个用来附加你的 View (Activity, Fragment 或任何显示你的数据) 到数据库中的持久化数据。您为创建的 View.
  2. 订阅一次

dbObservable
        .subscribeOn(Schedulers.io())
        .observeOn(AndroidSchedulers.mainThread())
        .subscribe(data -> {
            displayData(data);
        }, throwable -> {
            handleError(throwable);
        });
  1. API -> Database:另一个从 api 获取数据并将其保存在数据库中。每次要刷新数据库中的数据时,您都订阅它。

apiObservable
        .subscribeOn(Schedulers.io())
        .observeOn(Schedulers.io())
        .subscribe(data -> {
           storeDataInDatabase(data);
        }, throwable -> {
            handleError(throwable);
        });

编辑:

您不想 "transform" 将两个可观察值合二为一,这纯粹是因为您在问题中包含的原因。两个 observable 的行为完全不同。

Retrofit 中的 observable 就像 Single。它完成它需要做的事情,然后完成(onCompleted)。

Sqlbrite 的 observable 是一个典型的 Observable,它会在每次特定的 table 变化时发出一些东西。理论上应该会在未来完成。

当然你可以解决这个差异,但它会让你离拥有一个干净易读的代码很远很远。

如果你真的,真的需要公开一个observable,你可以隐藏你在订阅时实际上是从改造中订阅可观察的事实到您的数据库。

  1. 将 Api 订阅包装在一个方法中:

public void fetchRemoteData() {
    apiObservable
            .subscribeOn(Schedulers.io())
            .observeOn(Schedulers.io())
            .subscribe(data -> {
                persistData(data);
            }, throwable -> {
                handleError(throwable);
            });
}
  1. fetchRemoteData 订阅

dbObservable
        .subscribeOn(Schedulers.io())
        .observeOn(AndroidSchedulers.mainThread())
        .doOnSubscribe(() -> fetchRemoteData())
        .subscribe(data -> {
            displayData(data);
        }, throwable -> {
            handleError(throwable);
        });

我建议你认真考虑一下。因为您强迫自己处于需要单个可观察对象的位置这一事实可能会严重限制您。我相信这将是迫使您在未来改变观念的确切原因,而不是保护您免受改变本身的影响。