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