使用Realm的RxJava多线程 - 从错误的线程访问Realm

Gra*_*ith 8 java multithreading android realm rx-java

背景

我在我的应用程序中使用Realm.当加载数据时,它将经历密集处理,因此处理在后台线程上进行.

使用的编码模式是工作单元模式,而Realm仅存在于DataManager下的存储库中.这里的想法是每个存储库可以有不同的数据库/文件存储解决方案.

我试过了什么

下面是我在FooRespository类中的一些类似代码的示例.

这里的想法是获得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))));
}
Run Code Online (Sandbox Code Playgroud)

此代码稍后通过RxJava与繁重的处理代码一起使用:

dataManager.getFoosById(foo)
            .flatMap(this::processtheFoosInALongRunningProcess)
            .subscribeOn(Schedulers.io()) //could be Schedulers.computation() etc
            .subscribe(tileChannelSubscriber);
Run Code Online (Sandbox Code Playgroud)

阅读文档后,我的信念是上面应该有效,因为它不是异步的,因此不需要一个循环线程.我在同一个线程中获取了域的实例,因此它不在线程之间传递,也不是对象.

问题

当执行上述操作时,我得到了

领域从错误的线程访问.Realm对象只能在创建它们的线程上访问.

这似乎不对.我唯一能想到的是Realm实例池让我获得了一个使用主线程从另一个进程创建的现有实例.

Epi*_*rce 3

凯苏

return findFoosByIdQuery
        .findAll()
        .asObservable()
Run Code Online (Sandbox Code Playgroud)

这发生在 UI 线程上,因为这是您最初调用它的地方

.subscribeOn(Schedulers.io())
Run Code Online (Sandbox Code Playgroud)

啊啊啊然后你就在修改它们了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());
}
Run Code Online (Sandbox Code Playgroud)