Ben987654
Ben987654

Reputation: 3582

Realm is closed error within onSuccess callback of executeTransactionAsync

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

Answers (1)

EpicPandaForce
EpicPandaForce

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

Related Questions