Многопоточность 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);