Reputation: 1546
The following code throws an IllegalStateException that i am accessing realm on the wrong thread when i am trying to close the realm instance.
The exception is thrown when i am checking if the realm instance is closed in the closeRealm() method.
java.lang.IllegalStateException: Realm access from incorrect thread. Realm objects can only be accessed on the thread they were created.
at io.realm.BaseRealm.isClosed(BaseRealm.java:441)
at io.realm.Realm.isClosed(Realm.java:127)
at com.zeyad.usecases.db.RealmManager.lambda$closeRealm$24(RealmManager.java:309)
at com.zeyad.usecases.db.RealmManager$$Lambda$13.run(Unknown Source)
at android.os.Handler.handleCallback(Handler.java:746)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:148)
at android.os.HandlerThread.run(HandlerThread.java:61)
I am trying to implement an offline 1st approach and i am missing fixing the realm closing on the correct thread. Here is my code please check it out and let me know whats the problem. Or for a better look here is a link to the code: https://github.com/Zeyad-37/UseCases/blob/api/usecases/src/main/java/com/zeyad/usecases/api/DataService.java
Thanks in advance.
HandlerThread handlerThread = new HandlerThread("backgroundThread");
Handler backgroundHandler = new Handler(handlerThread.getLooper());
public void getUsersOfflineFirst() {
Observable<List> online = restApi.users().doOnNext(user -> {
Realm realm = Realm.getDefaultInstance();
try {
realm.copyToRealmOrUpdate(user)));
} finally {
closeRealm(realm);
}
});
return Observable.defer(() -> {
Realm realm2 = Realm.getDefaultInstance();
return realm2.where(User.class).findAll().asObservable()
.filter(results -> ((RealmResults) results).isLoaded())
.map(users -> realm2.copyFromRealm((RealmResults) users))
.doOnUnsubscribe(() -> closeRealm(realm2));
}).flatMap(new Func1<List, Observable<List>>() {
@Override
public Observable<List> call(List list) {
if (!list.isEmpty())
return Observable.just(list);
else return online;
}
}).compose(applySchedulers());
}
private void closeRealm(Realm realm) {
backgroundHandler.post(() -> {
if (!realm.isClosed()) {
realm.close();
Log.d(RealmManager.class.getSimpleName(), "realm instance closed!");
}
});
}
private <T> Observable.Transformer<T, T> applySchedulers() {
if (!handlerThread.isAlive())
handlerThread.start();
Scheduler backgroundThread = AndroidSchedulers.from(handlerThread.getLooper());
return observable -> observable.subscribeOn(backgroundThread)
.observeOn(AndroidSchedulers.mainThread())
.unsubscribeOn(backgroundThread);
}
Upvotes: 0
Views: 498
Reputation: 10267
The problem is probably not with the close but with the opening.
Both opening and closing Realm
instance should happen at the same thread, as in Realm, you must access Realm objects on the same thread that doesn't matter if it's close/open but any action on Realm Objects, as the docs says:
Realm, RealmObject or RealmResults instances cannot be passed across threads
My guess is that at the online
Observable
you are opening the Realm instance not on the handler thread (Realm realm = Realm.getDefaultInstance();
) while all your close are happens at the background handler thread.
This happens because you're online
Observable
is flatMapped and no Scheduler
is apply to it. so although the source Observable
(Observable.defer()
) is indeed subscribed as desired in the background thread, the online will subscribed at the thread that call the getUsersOfflineFirst()
method which is probably not the background thread.
Upvotes: 0