Reputation: 3582
I started getting new errors in my last release that I've never seen before, on code that hasn't been edited in many months.
Fatal Exception: java.lang.IllegalStateException This Realm instance has already been closed, making it unusable.
I have API calls that get data, and then write the content to Realm in the executeTransactionAsync
method, and then onSuccess, query realm for all records (as there may be others) which get returned in a callback.
api().getLocalGroups(1).enqueue(new Callback<LocalGroupBase>() {
@Override
public void onResponse(@NonNull Call<LocalGroupBase> call, @NonNull final Response<LocalGroupBase> response) {
try (Realm realm = setupRealm(context)) {
realm.executeTransactionAsync(new Realm.Transaction() {
@Override
public void execute(@NonNull Realm realm) {
realm.copyToRealmOrUpdate(response.body().getResults());
}
}, new Realm.Transaction.OnSuccess() {
@Override
public void onSuccess() {
callback.onLoaded(LocalGroup.getLocalGroups(realm));
}
});
}
}
@Override
public void onFailure(@NonNull Call<LocalGroupBase> call, Throwable t) {
callback.onError(t.getMessage());
}
});
public static RealmResults<LocalGroup> getLocalGroups(Realm realm) {
return realm.where(LocalGroup.class).findAllSorted("name");
}
It's using try with resources, so upon completion of the try statement, the realm should be closed, but it's occasionally crashing in the onSuccess callback on the realm query in getLocalGroups(realm)
This codes been like this for quite awhile, and it's happened in another spot as well now, so I'm confused as to what's changed (I updated Android build tools??) but also is this wrong, is realm actually close-able within the onSuccess?
Why would the realm be closed if I'm still within the try with resources?
This apps currently on Realm 4.4 and has been for a long time. We haven't had time to do the upgrade to 5 yet due to some breaking changes.
Upvotes: 1
Views: 672
Reputation: 81539
You were lucky that this didn't happen, have you tried quitting the app while this request was in progress? ;)
try (Realm realm = setupRealm(context)) { // <-- opens Realm realm.executeTransactionAsync(new Realm.Transaction() { @Override public void execute(@NonNull Realm realm) { ... // <-- runs in background asynchronously for some time } }, new Realm.Transaction.OnSuccess() { @Override public void onSuccess() { callback.onLoaded(LocalGroup.getLocalGroups(realm)); // <-- runs after background async transaction is complete } }); } // <-- closes Realm
Therefore, try-finally closes Realm immediately without waiting for async transaction to be complete, and apparently the Realm you "opened" (incremented local instance count of) was closed during the write transaction.
It is surprising that you got onSuccess
callback with the Realm having been closed though, I thought Realm swallowed the callback in this case...
Alas, the hack-fix for your solution would be:
api().getLocalGroups(1).enqueue(new Callback<LocalGroupBase>() {
@Override
public void onResponse(@NonNull Call<LocalGroupBase> call, @NonNull final Response<LocalGroupBase> response) {
try (Realm realm = setupRealm(context)) {
realm.executeTransactionAsync(new Realm.Transaction() {
@Override
public void execute(@NonNull Realm realm) {
realm.copyToRealmOrUpdate(response.body().getResults());
}
}, new Realm.Transaction.OnSuccess() {
@Override
public void onSuccess() {
try(Realm r = Realm.getDefaultInstance()) {
callback.onLoaded(LocalGroup.getLocalGroups(r));
}
}
});
}
}
@Override
public void onFailure(@NonNull Call<LocalGroupBase> call, Throwable t) {
callback.onError(t.getMessage());
}
});
public static RealmResults<LocalGroup> getLocalGroups(Realm realm) {
return realm.where(LocalGroup.class).findAllSorted("name");
}
Upvotes: 1