在使用copyFromRealm发送复制时,从错误的线程访问Realm会抛出异常。

6

当使用流式复制Realm对象而不是引用并在Schedulers.IO线程上观察它时,会出现著名的异常消息“从错误的线程访问Realm。 Realm对象只能在创建它们的线程中访问。”

这个复制品不应该是无线程的吗?我可以在一个线程上生成它并在另一个线程上处理它吗?

下面是我创建可观察对象的方式。

 public Observable<Brand> getAllBrands() {
    return realm.where(Brand.class)
            .findAll()
            .asObservable()
            .flatMap(Observable::from)
            .map(brand -> realm.copyFromRealm(brand));
}

以下是我对getAllBrands()函数的观察:
获取所有品牌函数。
 Observable<Brand> brandObservable = dataManager.getAllBrands();

    brandObservable.observeOn(AndroidSchedulers.mainThread())
            .subscribeOn(Schedulers.io())
            .subscribe(new Observer<Brand>() {
                @Override
                public void onCompleted() {
                    Log.d("reactive", "completed");
                }

                @Override
                public void onError(Throwable e) {
                    Log.d("reactive", e.getMessage());
                }

                @Override
                public void onNext(Brand brand) {
                    dataSource.add(brand.getName());
                    myAdapter.notifyDataSetChanged();
                }
            });

1
您使用的“realm”不在“io()”调度程序上。 - EpicPandaForce
2个回答

7
您正在使用 UI 线程上的 Realm 实例,并在 schedulers.io 上进行订阅:
realm.where(Brand.class)
        .findAll()
        .asObservable()
        .flatMap(Observable::from)
        .map(brand -> realm.copyFromRealm(brand)) // realm instance on the wrong thread
        .subscribeOn(schedulers.io());            

您需要的是一种将查询轻松移动到不同线程的方法,这个功能目前还在开发中: https://github.com/realm/realm-java/pull/1978。在此之前,您可以通过自己手动实现以下方式来解决:
public Observable<Brand> getAllBrands(final Realm realm) {
    return Observable.create(new Observable.OnSubscribe<List<Brand>>() {
        @Override
        public void call(final Subscriber<? super List<Brand>> subscriber) {
            Realm obsRealm = Realm.getInstance(realm.getConfiguration());
            final RealmResults<Brand> results = obsRealm.where(Brand.class).findAll();
            final RealmChangeListener listener = new RealmChangeListener() {
                @Override
                public void onChange() {
                    subscriber.onNext(realm.copyFromRealm(results));
                }
            };
            results.addChangeListener(listener);
            subscriber.add(Subscriptions.create(new Action0() {
                @Override
                public void call() {
                    realm.removeChangeListener(listener);
                    realm.close();
                }
            }));

        }
    })
    .flatMap(Observable::from);
}

请注意,Realm变更侦听器仅在Looper线程上工作,这意味着您可能需要将工作线程更改为H。
HandlerThread bgThread = new HandlerThread("workerThread");
Handler handler = new Handler(bgThread.getLooper());

getAllBrands(realm).subscribeOn(HandlerScheduler.from(handler));

我并不是试图在不同的线程之间移动查询。相反,我正在尝试流式传输已分离的 Realm 对象的副本。无论创建它的 realm 实例如何,难道不能跨多个线程使用已分离的数据吗? - sam33r
这是一个问题,因为在 map(brand -> realm.copyFromRealm(brand)); 中,brand 是在 UI 线程上创建的对象,并在 schedulers.io() 线程上访问。我的解决方案是通过在 schedulers.io() 线程上执行查询来解决这个问题(采用一种变通方法)。 - Christian Melchior
现在我收到了“无法从非Looper线程注册侦听器”的错误提示。 - sam33r
嗨@Augusent,你有没有得到你的问题的答案?我也在想同样的事情。 - John Ernest Guadalupe
为什么会有 onComplete 的调用?它让我的可观察对象持续加载。 - John Ernest Guadalupe
显示剩余2条评论

0

问题

我可以在一个线程中生成它,然后在另一个线程中处理它吗?

答案是否定的。您可以在这里阅读Realm的描述。

尽管Realm文件可以被多个线程同时访问,但是您不能在不同的线程之间传递Realms、Realm对象、查询和结果。线程示例展示了如何在多线程环境下使用Realm。


2
但问题是关于使用 copyFromRealm() 的分离对象,这些对象据说与 Realm 实例无关。 - EpicPandaForce

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接