Многопоточность RxJava с доступом Realm-Realm из неправильного потока
фон
Я использую Realm в своем приложении. Когда данные загружаются, они подвергаются интенсивной обработке, поэтому обработка происходит в фоновом потоке.
используемый шаблон кодирования является единицей шаблона работы, и область существует только в репозитории в окне DataManager. Идея здесь заключается в том, что каждый репозиторий может иметь другое решение для хранения базы данных/файлов.
что у меня есть пробовал
Ниже приведен пример некоторого подобного кода к тому, что у меня есть в моем классе FooRespository.
идея здесь заключается в том, что получен экземпляр Realm, используемый для запроса области для объектов, представляющих интерес, возврата их и закрытия экземпляра realm. Обратите внимание, что это синхронно и в конце копирует объекты из Realm в неуправляемое состояние.
public Observable<List<Foo>> getFoosById(List<String> fooIds) {
Realm realm = Realm.getInstance(fooRealmConfiguration);
RealmQuery<Foo> findFoosByIdQuery = realm.where(Foo.class);
for(String id : fooIds) {
findFoosByIdQuery.equalTo(Foo.FOO_ID_FIELD_NAME, id);
findFoosByIdQuery.or();
}
return findFoosByIdQuery
.findAll()
.asObservable()
.doOnUnsubscribe(realm::close)
.filter(RealmResults::isLoaded)
.flatMap(foos -> Observable.just(new ArrayList<>(realm.copyFromRealm(foos))));
}
этот код позже используется в сочетании с тяжелым кодом обработки через RxJava:
dataManager.getFoosById(foo)
.flatMap(this::processtheFoosInALongRunningProcess)
.subscribeOn(Schedulers.io()) //could be Schedulers.computation() etc
.subscribe(tileChannelSubscriber);
после прочтения документов я считаю, что вышеизложенное должно работать, поскольку оно не асинхронно и поэтому не нуждается в потоке looper. Я получаю экземпляр realm в одном потоке, поэтому он не передается между потоками и объектами.
проблема
когда выполняется вышеизложенное, я получаю
доступ к области из неправильного потока. Доступ к объектам Realm возможен только на нити они были созданы.
это не кажется правильным. Единственное, о чем я могу думать, это то, что пул экземпляров Realm получает мне существующий экземпляр, созданный из другого процесса с использованием основного потока.
2 ответов
Кей так
return findFoosByIdQuery
.findAll()
.asObservable()
это происходит в потоке пользовательского интерфейса, потому что именно оттуда вы его вызываете изначально
.subscribeOn(Schedulers.io())
Аааа и тогда вы возитесь с ними на Schedulers.io()
.
нет, это не тот же поток!
насколько мне не нравится подход копирование С ноль-копия база данных, ваш текущий подход пронизан проблемами из-за неправильного использования realmResults.asObservable()
, так вот спойлер для того, что ваш код должен быть:
public Observable<List<Foo>> getFoosById(List<String> fooIds) {
return Observable.defer(() -> {
try(Realm realm = Realm.getInstance(fooRealmConfiguration)) { //try-finally also works
RealmQuery<Foo> findFoosByIdQuery = realm.where(Foo.class);
for(String id : fooIds) {
findFoosByIdQuery.equalTo(FooFields.ID, id);
findFoosByIdQuery.or(); // please guarantee this works?
}
RealmResults<Foo> results = findFoosByIdQuery.findAll();
return Observable.just(realm.copyFromRealm(results));
}
}).subscribeOn(Schedulers.io());
}
обратите внимание, что вы создаете экземпляр вне всего конвейера обработки RxJava. Таким образом, в главном потоке (или в любом потоке, в котором вы находитесь, при вызове getFoosById()
.
только потому, что метод возвращает наблюдаемое, не означает, что он работает в другом потоке. Только конвейер обработки наблюдаемого, созданный последним утверждением вашего getFoosById()
метод выполняется в правильном потоке (filter()
на flatMap()
и вся обработка сделанная абонент.)
таким образом, вы должны убедиться, что вызов getFoosById()
уже сделано в потоке, используемом Schedulers.io()
.
один из способов достичь этого-использовать Observable.defer()
:
Observable.defer(() -> dataManager.getFoosById(foo))
.flatMap(this::processtheFoosInALongRunningProcess)
.subscribeOn(Schedulers.io()) //could be Schedulers.computation() etc
.subscribe(tileChannelSubscriber);