Realm-java: Ability to "unsubscribe" explicitly from RealmResults without relying on finalizer daemon

Created on 7 Jun 2017  路  16Comments  路  Source: realm/realm-java

Goal

What do you want to achieve?

Create an auto-complete view where the RealmResults is replaced when a new keyword is searched for.

Expected Results

The previous RealmResults is no longer evaluated/updated because it is no longer needed.

Actual Results

It depends on GC and finalizer whether RealmResults is still kept alive in Realm or not.

Version of Realm and tooling

Realm version(s): 3.4.0-SNAPSHOT

Realm sync feature enabled: no

Android Studio version: .

Which Android version and device: .

Most helpful comment

But what if I remove change listener and then add change listener? I assume that triggers an async reload and it will be called when re-evaluated.

yup, that will be no diff compared with adding the listener at the first place.

All 16 comments

You mean unsubscribe() does not work?

There is an unsubscribe?

there is RealmResults.removeAllChangeListeners().

@Zhuinden I assume you want to do this to optimize system resources? Because otherwise just unregistering the listener should be enough, but you are correct that internally we will continue to keep the RealmResults up to date until it gets GC'ed.

Only way to do this would be to add an explicit close() method I think, but to be honest I'm not sure if it is worth the effort. Do you have any measurements that show a performance problem?

EDIT: Is everybody else also compiling? Everybody jumps to Github at the same time 馃槅

you are correct that internally we will continue to keep the RealmResults up to date until it gets GC'ed.

yes, this is what I want to kill, because if you use the synchronous API then autocomplete creates jitter, and there is no reason for Realm to update a result set that I specifically know that I no longer need

Technically last time I checked (although it was a while ago), it was in the internal RealmContext that is private, so theoretically removing it from there by hand with reflection is possible, but it's a pain.

I've always missed this ability from the Realm API 馃槃

I think I have seen other use cases where this might be handy. People working with highly dynamic schemes have run into issues where an "old" RealmResult would suddenly crash because the schema underneath has changed. Having a close method there would also prevent these cases.

My only concern is that a close method will make things look more complicated, but then again calling it will be entirely optional, so perhaps I'm just overthinking it.

To be consistent we should probably add it to RealmList, RealmResults and RealmObject. Putting it on the ManagedObject interface that @bmeike just created might make sense. What do you think @realm/java ?

Actually if there is no listeners on the RealmResults, the background thread won't update it anymore.
Also if the RealmResults has not been accessed for a while, Object Store will stop updating it https://github.com/realm/realm-object-store/blob/master/src/results.cpp#L627

I don't think close() would be the right to solve the schema changes, if the RealmResults cannot be accessed anymore because of schema changes, then it should just be invalid.

Ah, I didn't know that, but that begs the question, what happens if:

// Current behaviour
RealmResults<Person> results = getResults();
results.removeAllChangeListeners();
realm.refresh();
results.first(); // I cannot see how we can avoid not having updated it here?

// With close() method
RealmResults<Person> results = getResults();
results.close();
realm.refresh();
results.first(); // Throw invalid state exception

realm.refresh();
results.first(); // I cannot see how we can avoid not having updated it here?

First, the realm.resfresh() won't re-run the query since it doesn't have any listeners.
It will just re-run the query synchronically when results.first() called and give you the first element if the results is valid.

I don't see any reason why would user wants to call close()

The theory is very simple, if you don't have listeners, then you cannot benefit from the async-query since there is no way to notify you that the query is done.

Hmm, since calling refresh() will convert all asynchronous queries to synchronous queries, you might be right. I should probably not try to think about this while debugging other issues :)

What about this really corner casy code:

final RealmResults<Person> persons = realm.where(Person.class).findAllAsync();
final RealmResults<Dog> dogs = realm.where(Dog.class).findAllAsync();
dogs.addChangeListner(results -> {
  persons.first(); // This will be converted to a synchronous query?
});

I don't think so.
When the listener on dogs called, all the queries have been executed and handover to the caller thread. persons.first() will just give the first element.

PS. there is something called update_table_view_if_needed() in the Object Store to decide if the query needs to be re-run on current realm version.

So, if your code logic is driven by the listeners and never call the refresh(), then you have nothing to worry about. All the queries will just be executed in the background thread.

Actually if there is no listeners on the RealmResults, the background thread won't update it anymore.

Well, Object Store Results integration fixed more quirks than I thought, then removeAllChangeListeners will also stop updating.

But what if I remove change listener and then add change listener? I assume that triggers an async reload and it will be called when re-evaluated.

Okay, makes sense, thank you for the answer I didn't know OS does this optimization out of the box 馃憤

But what if I remove change listener and then add change listener? I assume that triggers an async reload and it will be called when re-evaluated.

yup, that will be no diff compared with adding the listener at the first place.

Was this page helpful?
0 / 5 - 0 ratings