【问题标题】:Refreshing data using SQLBrite + Retrofit使用 SQLBrite + Retrofit 刷新数据
【发布时间】:2017-01-11 16:57:56
【问题描述】:

这是我的用例:

我正在开发一个应用程序,它通过 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);
            })
})

看起来它工作正常,但它看起来并不优雅和“正确”。

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

【问题讨论】:

    标签: android rx-java retrofit2 sqlbrite


    【解决方案1】:

    如果你稍微改变一下思维方式,你的问题的解决方案实际上是超级简单和干净的。我正在使用完全相同的数据交互(Retrofit + Sqlbrite),而且这个解决方案效果很好。

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

    1. Database -> View:这个用于将您的ViewActivityFragment 或任何显示您的数据)附加到数据库中的持久数据。您为创建的View 订阅一次。

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

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

    编辑:

    您不想将两个可观察对象“转换”为一个,纯粹是因为您在问题中包含的原因。两个 observables 的行为完全不同。

    Retrofit 中的observable 就像Single。它会做它需要做的事情,并完成(使用onCompleted)。

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

    你可以解决这个差异,但它会让你远离干净且易于阅读的代码。

    如果您真的,真的需要公开一个 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);
            });
    

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

    【讨论】:

    • 你如何抽象这个概念来隐藏你使用两个独立订阅的事实?我现在设置的方法是让控制器调用一个方法,返回一个Observable,这样做。这可以用你建议的方法来完成吗?
    • 问题是每次实际加载数据时都必须刷​​新数据。另一个原因是,将来可能必须开发适当的离线模式(基于服务器端数据更改通知或类似的东西),我希望将这种更改的范围保持在最小。在这种情况下,暴露我们有两个独立的数据源这一事实可能是个问题。
    • 编辑中的解决方案看起来不错。我只有最后一个问题 - fetchRemoteData() 方法中可能发生的错误会发生什么?如果我公开一个可观察对象,那么能够将所有传入的错误重定向到生成的单个订阅者会很好。你认为这是个好主意吗?
    • @DanailAlexiev 我认为这不是一个好主意。正如我所说,您希望对 Db 的订阅有点“永久”。如果 Retrofit 流中有错误,并且您将该错误传递给 Db 流,您最终将终止 Db 订阅。它将无法接收更多更新(排放)。而且我相信这不是理想的情况。如果 api 出现错误(例如您离线),您不希望您的 Db 订阅终止。当您的用户修复连接并刷新时,活动的 Db 订阅将轻松处理所有事情(并且不会终止)。
    • 感谢您的宝贵时间和意见
    猜你喜欢
    • 1970-01-01
    • 2019-12-29
    • 1970-01-01
    • 1970-01-01
    • 2017-10-17
    • 2016-10-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多