no news
no news

Reputation: 1070

Realm with RxJava2 Flowable incorrect thread

I am trying to understand how work with Realm and RxJava2together (for async transactions) and did some sample project with transactions:

 private void writeAllUsers() {
        Realm.getDefaultInstance().executeTransactionAsync(realm -> realm.copyToRealmOrUpdate(users));
    }

    private void getAllUsers() {
        getUsers().observeOn(AndroidSchedulers.mainThread())
                .subscribeOn(Schedulers.io())
                .subscribe(this::successGetUser, this::handleUserError);
    }

    private Flowable<RealmResults<User>> getUsers() {
        return Realm.getDefaultInstance()
                .where(User.class)
                .findAllAsync()
                .asFlowable();
    }

But when I call getAllUsers, I get exception:

java.lang.IllegalStateException: Realm access from incorrect thread. Realm objects can only be accessed on the thread they were created.

What I did wrong in this case?

Upvotes: 0

Views: 1303

Answers (1)

EpicPandaForce
EpicPandaForce

Reputation: 81588

RealmResults<T> represents a thread-local collection of proxy views, where due to Realm's MVCC architecture, they are bound to the current thread-local version of the database.

This means that a managed RealmResults or Realm or RealmObject cannot be passed between threads.

So you cannot and mustn't do this with a RealmResults :

getUsers()
.observeOn(AndroidSchedulers.mainThread())

.subscribeOn(Schedulers.io())

Passing managed results from io to main thread is no-no!


asFlowable just wraps the RealmChangeListener (only valid on looper threads) as a Flowable, so thread confinement is still a thing.

In fact, it will never terminate, and you should make sure you unsubscribe.

So to hide the results as Flowable in such a way that it works on any thread, assuming you don't do thread jumps with it:

private Flowable<RealmResults<User>> getUsers(Realm realm) {
    if(realm.isAutoRefresh()) {
       return realm
            .where(User.class)
            .findAllAsync()
            .asFlowable()
            .filter(RealmResults::isLoaded);
    } else {
       return Flowable.just(realm
              .where(User.class)
              .findAll());
    }
}

Reading on background looper thread and passing unmanaged results on change using copyFromRealm() is slightly trickier, you should not use copyFromRealm() on a RealmResults on a UI thread.

Upvotes: 1

Related Questions