Обновление данных с использованием SQLBrite + Retrofit
Вот мой вариант использования:
Я разрабатываю приложение, которое связывается с сервером через REST API и сохраняет полученные данные в базе данных SQLite (оно использует его в качестве кеша некоторых типов).
Когда пользователь открывает экран, должно произойти следующее:
- Данные загружаются из БД, если таковые имеются.
- Приложение вызывает API для обновления данных.
- Результат вызова API сохраняется в БД.
- Данные перезагружаются из БД при перехвате уведомления об изменении данных.
Это очень похоже на случай, представленный здесь, но есть небольшая разница.
Поскольку я использую 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), и это решение работает отлично.
Что вам нужно сделать, это использовать две отдельные наблюдаемые подписки, которые заботятся о совершенно разных процессах.
Database
->
View
: Этот используется для прикрепления вашегоView
(Activity
,Fragment
или что-либо отображает ваши данные) для постоянных данных в дБ. Вы подписываетесь на него раз для созданногоView
,
dbObservable
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(data -> {
displayData(data);
}, throwable -> {
handleError(throwable);
});
API
->
Database
Другой, который извлекает данные из API и сохраняет их в БД. Вы подписываетесь на него каждый раз, когда хотите обновить свои данные в базе данных.
apiObservable
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.io())
.subscribe(data -> {
storeDataInDatabase(data);
}, throwable -> {
handleError(throwable);
});
РЕДАКТИРОВАТЬ:
Вы не хотите "преобразовывать" обе наблюдаемые в одну, просто по той причине, что вы указали в своем вопросе. Обе наблюдаемые действуют совершенно по-разному.
observable
от модернизации действует как Single
, Он делает то, что ему нужно, и заканчивает (с onCompleted
).
observable
из Sqlbrite является типичным Observable
, он будет что-то испускать каждый раз, когда меняется конкретная таблица. Теоретически это должно закончиться в будущем.
Конечно, вы можете обойти эту разницу, но это уведет вас далеко от того, чтобы иметь чистый и легко читаемый код.
Если вы действительно, действительно нужно выставить один observable
, вы можете просто скрыть тот факт, что вы подписываетесь на наблюдаемое от модернизации при подписке на вашу базу данных.
- Заверните подписку Api одним из следующих способов:
public void fetchRemoteData() {
apiObservable
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.io())
.subscribe(data -> {
persistData(data);
}, throwable -> {
handleError(throwable);
});
}
fetchRemoteData
по подписке
dbObservable
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.doOnSubscribe(() -> fetchRemoteData())
.subscribe(data -> {
displayData(data);
}, throwable -> {
handleError(throwable);
});
Я предлагаю вам действительно подумать обо всем этом. Потому что тот факт, что вы вгоняете себя в положение, в котором вам нужна единственная наблюдаемая, может сильно вас ограничивать. Я верю, что это будет именно то, что заставит вас изменить свою концепцию в будущем, вместо того, чтобы защитить вас от самого изменения.