Обновление данных с использованием SQLBrite + Retrofit

Вот мой вариант использования:

Я разрабатываю приложение, которое связывается с сервером через REST API и сохраняет полученные данные в базе данных SQLite (оно использует его в качестве кеша некоторых типов).

Когда пользователь открывает экран, должно произойти следующее:

  1. Данные загружаются из БД, если таковые имеются.
  2. Приложение вызывает API для обновления данных.
  3. Результат вызова API сохраняется в БД.
  4. Данные перезагружаются из БД при перехвате уведомления об изменении данных.

Это очень похоже на случай, представленный здесь, но есть небольшая разница.

Поскольку я использую SQLBrite, наблюдаемые базы данных не заканчиваются (потому что есть ContentObserver зарегистрирован там, что выталкивает новые данные вниз по течению), поэтому такие методы, как concat, mergeи т. д. не сработает.

В настоящее время я решил эту проблему, используя следующий подход:

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);
            })
})

Кажется, что все работает хорошо, но это не кажется элегантным и "правильным".

Можете ли вы предложить или указать мне ресурс, который объясняет, как лучше всего справиться с этой ситуацией?

1 ответ

Решение

Решение вашей проблемы на самом деле очень простое и чистое, если вы немного измените образ мышления. Я использую точно такое же взаимодействие с данными (Retrofit + Sqlbrite), и это решение работает отлично.

Что вам нужно сделать, это использовать две отдельные наблюдаемые подписки, которые заботятся о совершенно разных процессах.

  1. Database->View: Этот используется для прикрепления вашего View (Activity, Fragment или что-либо отображает ваши данные) для постоянных данных в дБ. Вы подписываетесь на него раз для созданного View,

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);
        });

РЕДАКТИРОВАТЬ:

Вы не хотите "преобразовывать" обе наблюдаемые в одну, просто по той причине, что вы указали в своем вопросе. Обе наблюдаемые действуют совершенно по-разному.

observable от модернизации действует как Single, Он делает то, что ему нужно, и заканчивает (с onCompleted).

observable из Sqlbrite является типичным 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);
        });

Я предлагаю вам действительно подумать обо всем этом. Потому что тот факт, что вы вгоняете себя в положение, в котором вам нужна единственная наблюдаемая, может сильно вас ограничивать. Я верю, что это будет именно то, что заставит вас изменить свою концепцию в будущем, вместо того, чтобы защитить вас от самого изменения.

Другие вопросы по тегам