从错误的线程

时间:2017-06-17 18:15:26

标签: android realm rx-java rx-java2

我从Realm

获得此异常读/写
  

06-19 09:49:26.352 11404-11404 / ****** E / ContentValues:loadData:OnError Realm从错误的线程访问。 Realm对象只能在创建它们的线程上访问。                                                                   java.lang.IllegalStateException:从错误的线程访问Realm。 Realm对象只能在创建它们的线程上访问。                                                                       at io.realm.BaseRealm.checkIfValid(BaseRealm.java:385)                                                                       at io.realm.RealmResults.isLoaded(RealmResults.java:115)                                                                       at io.realm.OrderedRealmCollectionImpl.size(OrderedRealmCollectionImpl.java:307)                                                                       在io.realm.RealmResults.size(RealmResults.java:60)                                                                       at java.util.AbstractCollection.isEmpty(AbstractCollection.java:86)                                                                       at / ****** .lambda $ loadData $ 0(SplashPresenter.java:42)                                                                       at / ****** $$ Lambda $ 1.test(未知来源)                                                                       at io.reactivex.internal.operators.observable.ObservableFilter $ FilterObserver.onNext(ObservableFilter.java:45)                                                                       at io.reactivex.observers.SerializedObserver.onNext(SerializedObserver.java:111)                                                                       at io.reactivex.internal.operators.observable.ObservableDelay $ DelayObserver $ 1.run(ObservableDelay.java:84)                                                                       at io.reactivex.internal.schedulers.ScheduledRunnable.run(ScheduledRunnable.java:59)                                                                       at io.reactivex.internal.schedulers.ScheduledRunnable.call(ScheduledRunnable.java:51)                                                                       在java.util.concurrent.FutureTask.run(FutureTask.java:237)                                                                       at java.util.concurrent.ScheduledThreadPoolExecutor $ ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:272)                                                                       at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1133)                                                                       at java.util.concurrent.ThreadPoolExecutor $ Worker.run(ThreadPoolExecutor.java:607)                                                                       在java.lang.Thread.run(Thread.java:761)

这是代码:

  mSubscribe = Observable.just(readData())
            .delay(DELAY, TimeUnit.SECONDS)
            .filter(value -> !value.isEmpty())
            .switchIfEmpty(createRequest())
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread()).subscribe(data -> {
                getView().hideLoading();
                writeData(data);
            }, 
           (throwable -> {
            }));

读取数据

  private List<CategoryModel> readData() {
    Realm defaultInstance = Realm.getDefaultInstance();
    List<CategoryModel> title = defaultInstance.where(CategoryModel.class).findAllSorted("title");

    defaultInstance.close();
    return title;
}

写入数据

private void writeData(List<CategoryModel> categoryModels) {

        try {
            Realm defaultInstance = Realm.getDefaultInstance();
            defaultInstance.executeTransactionAsync(realm -> realm.insertOrUpdate(categoryModels));
            defaultInstance.close();
        } finally {
            getView().notifyActivity(categoryModels);
        }
    }

如何使用正确的线程来遵循此逻辑?

4 个答案:

答案 0 :(得分:6)

The only rule to using Realm across threads is to remember that Realm, RealmObject or RealmResults instances cannot be passed across threads

如果要从不同的线程访问相同的数据,只需获取一个新的Realm实例(即Realm.getDefaultInstance())并通过查询获取对象(然后关闭Realm)在线程的最后)。

对象将映射到磁盘上的相同数据,并且可读和可写任何线程!您还可以使用realm.executeTransactionAsync() if(document.body.contains(yourElement)) { // Yep, it's attached. } 在后​​台线程上运行代码

答案 1 :(得分:2)

  

我如何使用正确的线程来遵循这个逻辑?

通过不尝试在Schedulers.io()上读取您的UI线程(Realm提供自动更新延迟加载的代理视图,毕竟在UI线程上为您的数据提供更改通知)。

所以不是这个

 mSubscribe = Observable.just(readData())
        .delay(DELAY, TimeUnit.SECONDS)
        .filter(value -> !value.isEmpty())
        .switchIfEmpty(createRequest())
        .subscribeOn(Schedulers.io())
        .observeOn(AndroidSchedulers.mainThread()).subscribe(data -> {
            getView().hideLoading();
            writeData(data);
        }, 
       (throwable -> {
        }));

private List<CategoryModel> readData() {
    Realm defaultInstance = Realm.getDefaultInstance();
    List<CategoryModel> title = defaultInstance.where(CategoryModel.class).findAllSorted("title");

    defaultInstance.close();
    return title;
}

private void writeData(List<CategoryModel> categoryModels) {
    try {
        Realm defaultInstance = Realm.getDefaultInstance();
        defaultInstance.executeTransactionAsync(realm -> realm.insertOrUpdate(categoryModels));
        defaultInstance.close();
    } finally {
        getView().notifyActivity(categoryModels);
    }
}

你应该有像

这样的东西
private Observable<List<CategoryModel>> readData() { // Flowable with LATEST might be better.
    return io.reactivex.Observable.create(new ObservableOnSubscribe<List<CategoryModel>>() {
        @Override
        public void subscribe(ObservableEmitter<List<CategoryModel>> emitter)
                throws Exception {
            final Realm observableRealm = Realm.getDefaultInstance();
            final RealmResults<CategoryModel> results = observableRealm.where(CategoryModel.class).findAllSortedAsync("title");
            final RealmChangeListener<RealmResults<CategoryModel>> listener = results -> {
                if(!emitter.isDisposed() && results.isLoaded()) {
                    emitter.onNext(results);
                }
            };

            emitter.setDisposable(Disposables.fromRunnable(() -> {
                if(results.isValid()) {
                    results.removeChangeListener(listener);
                }
                observableRealm.close();
            }));
            results.addChangeListener(listener);
        }
    }).subscribeOn(AndroidSchedulers.mainThread())
            .unsubscribeOn(AndroidSchedulers.mainThread());
}

private void setSubscription() {
    mSubscribe = readData()
            .doOnNext((list) -> {
                if(list.isEmpty()) {
                    Single.fromCallable(() -> this::createRequest)
                            .subscribeOn(Schedulers.io())
                            .subscribe((data) -> {
                                writeData(data);
                            });
                }
            }).subscribe(data -> {
                if(!data.isEmpty()) {
                    getView().hideLoading();
                    getView().notifyActivity(data);
                }
            }, throwable -> {
                throwable.printStackTrace();
            });
}

private void writeData(List<CategoryModel> categoryModels) {
    try(Realm r = Realm.getDefaultInstance()) {
        r.executeTransaction(realm -> realm.insertOrUpdate(categoryModels));
    }
}

void unsubscribe() {
    mSubscribe.dispose();
    mSubscribe = null;
}

这种方式(如果我没有弄乱任何东西),你最终得到了herehere所描述的被动数据层,除了没有额外的开销来映射整个结果

修改

从Realm 4.0开始,可以直接将RealmResults公开为Flowable(在UI线程或后台循环线程上)。

public Flowable<List<MyObject>> getLiveResults() {
    try(Realm realm = Realm.getDefaultInstance()) {
        return realm.where(MyObject.class) 
                    .findAllAsync()
                    .asFlowable()
                    .filter(RealmResults::isLoaded);
    }
}

答案 2 :(得分:0)

您需要从领域对象中提取所需数据到POJO并使用map运算符发出POJO,以便视图对象可以使用来自领域的数据在Android主线程上使用pojo进行更新。

答案 3 :(得分:-1)

您只能在事务中操作Realm对象,或者只能在读/写这些对象的线程中操作。在您的情况下,您从readData方法获取RealmResult并使用RxJava切换导致异常的线程。使用copyFromRealm从realm获取数据,它将它们作为普通对象而不是realm对象返回。