Realm-java: RealmChangeListener and changes of nested objects

Created on 14 Jun 2017  路  38Comments  路  Source: realm/realm-java

Goal

Detect changes of nested objects of a single RealmObject using RealmChangeListener .
Understand the differences between RealmChangeListener on RealmObject and RealmResults.

Expected Results

RealmChangeListener of a single RealmObject would listen to changes of its nested objects.
RealmChangeListener of a single RealmObject would listen to more changes than the RealmChangeListener of RealmResults.

Actual Results

RealmChangeListener of a single RealmObject doesn't listen to changes of its nested objects.
RealmChangeListener of RealmResults listens to changes of its children's nested objects.

Steps & Code to Reproduce

I created a simple Android project to reproduce my use case:
https://github.com/jpmcosta/RealmTestProject/tree/262ee9099b47852c2b26bc2f7e3874c8670e6264

Schema:

  • App object contains a RealmColor property. That's just a (bad) pattern to illustrate the issue.

How to reproduce the issue:

  • there's a list with a single App object
  • click on that object to randomly change the value of its RealmColor property
  • when RealmColor.value changes we want to:

    • change the App text color

    • change the FAB color

To change the App text color we add RealmChangeListener to RealmResults<App> and call notifyDatasetChanged when a change is detected.
To change the FAB color we add a RealmChangeListener to the App object and update its color when a change is detected.

Issue:

  • The FAB color is not updated properly.

Version of Realm and tooling

Realm version: 3.3.2

O-Community Pipeline-Idea-Backlog T-Feature

Most helpful comment

@Zhuinden yes, that would work. I understand that App itself has not changed. But then, why does the RealmChangeListener in RealmResults<App> detect a change? Also, if I have 10 nested objects, should I need to add 10 different listeners?

All 38 comments

I guess what I'm requesting is to have a way of listening to nested objects changes when adding a RealmChangeListener to a single RealmObject. A parameter would suffice.

To change the FAB color we add a RealmChangeListener to the App object and update its color when a change is detected.

You need to add it to the RealmColor object, which also needs to be retained as field reference.

https://github.com/realm/realm-java/issues/4598#issuecomment-299401156

@Zhuinden yes, that would work. I understand that App itself has not changed. But then, why does the RealmChangeListener in RealmResults<App> detect a change? Also, if I have 10 nested objects, should I need to add 10 different listeners?

Well as you said, you could also refer to your single object with a RealmResults and then you'll get nested link notifications, as specified in that comment I linked.

Of course, I personally don't know why this is how the notification behavior works.

@Zhuinden for some reason I missed the comment you linked. Sorry about that.
Regarding to referring to my single object with a RealmResults, how would that work? Would I need to query for the object with a findAll call, or is there a better way?
In my real use case I want to update a state based on the first element of a list. That could complicate things quite a bit.

Yeah, realm.where(MyObject.class).equalTo("id", myObjectId).findAll(); works, and if it's empty then it's not there, and if it's not empty then the first object is your object.

Thanks for your help, but I'm still not sure about using a query to keep a deep listener on the first item of a list.
That would require to requery every time the first item changes (it's common), and I'm not sure about the implications on performance.
I still think it would be best to have a parameter to specify whether or not the listener should detect nested objects changes, but that would require some changes to realm-java.
Thanks for the answer. 馃檪

Edit: if you don't think it's worth discussing RealmChangeListener behavior at the current time, feel free to close the issue, as my use case is already solved. 馃憤

I believe we have an issue somewhere for listening to specific fields, including linked fields. That sounds like something similar to this. I'll try to find it.

@cmelchior I think I detected a case where RealmChangeListener is called, but there weren't any changes to the object (or its children for that matter).
I still haven't found time to reproduce it consistently. If I do, should I update this issue, or open a new one?

Current Object Store implementation doesn't support notifications of Object's linked field changes. Maybe there are some concerns from @tgoyne to support that?

I've come along this and want to get notified if a nested object changes (so I can update the UI showing the nested object).

I had the listener attached to RealmObject (just like in the question here) which does not work (as mentioned) but when using findAllAsync() to get a RealmResult it doesn't get notified either.

So no luck for me using this workaround 馃槩.
I'm using Realm 2.0.1 4.3.0.


Has this changed lately? How can I get notified about changed on nested objects?

Is it related to the use of findAllAsync() instead of the normal findAll()? I've linked the async-result to be a LiveData so it can be used with a ViewModel (which works nice except of this nested-listener-problem).

2.0.1 is like super old o_o

In fact, in Realm before 3.0.0, all change listeners that belong to a RealmResults should have been called for the change of any linked object property change.

Oh yea, my fault - using 4.3.0 (copy & paste error...)

findAllAsync should be called for linked objects. Do you have a sample that reproduces this? How deep links are we talking about?

Basically I have the following:

- Foo
 - id
 - name
 - RealmList<Bar>

- Bar
 - id
 - name

I'm fetching all Foo with findAllAsync() and hook the listener to this result.
When now Bar.name changes, I want the listener to be called (which it isn't). This does not work for findFirstAsync() nor findAllAsync().

Ok, some updates:

I've tried findFirst(), findFirstAsync(), findAll() and findAllAsync() and it looks like only findAll() calls the listener for changes on the nested object.

So I'd like to extend this issue to also update the ...Async()-methods to call the listener like the non-async version.

@beeender can this be possible?

@hardysim @Zhuinden

This is documented, but maybe it is a bit difficult to find?

If a field points to another RealmObject this listener will only be triggered if the field is set to a new object or null. Updating the referenced RealmObject will not trigger this listener.

If a field points to a RealmList, this listener will only be triggered if one or multiple objects are inserted, removed or moved within the List. Updating the objects in the RealmList will not trigger this listener.

https://realm.io/docs/java/4.3.1/api/io/realm/RealmObjectChangeListener.html#onChange-T-io.realm.ObjectChangeSet-

Ah, ok - I had a look at the addListener()-Methods only and not at the doc of the listener itself (hard to see using lambdas).

So no chance for the listeners on single objects (hence this issue here).


But for the lists: It's working for the non-async-method - can we get it for async as well? Should I open a new issue for this?

@beeender my question was that they say Async method doesn't get called for linked object change, but that's not expected behavior. Especially considering internally the only difference is whether query is immediately loaded or not.

@Zhuinden

my question was that they say Async method doesn't get called for linked object change, but that's not expected behavior. Especially considering internally the only difference is whether query is immediately loaded or not.
I've tried findFirst(), findFirstAsync(), findAll() and findAllAsync() and it looks like only findAll() calls the listener for changes on the nested object.

This doesn't seem to be possible

Then it's probably just that findAllAsync was tested in such a way that RealmResults was not stored as field variable and then GC ate it and notification didn't happen

Any solution I have the same problem, there is object class with field RealmList, listener on query of the highest hierarchy object with findAllAsync, but not register changes on the the field RealmList, and the results of the query, when I get RealmList from the object is very messed.
With query with findAll is worked correctly.

You probably just don't keep a strong reference to the RealmList.

How it should keep strong reference, I am new in Realm

Just like https://stackoverflow.com/a/43178078/2413303 it also applies for RealmList

I see, but I think that's not my case, let say I use the previous example

  • Foo

    • id

    • name

    • RealmList bars

  • Bar

    • id

    • name

I make query RealmResults results= realm.where(Foo.class).findAllAsync().
Then I add listener on results when onChange event is triggered, in the event body I try to access results.getbars(), the list of Bar is messed

I don't understand what is messed means, please provide a definition or a reproducible sample.

Messed means. when I try to get the bar list and read, one time the name of bar is AtJohny, the other time is Anthony, I get objects in realmlist not like I put, I get them randomly

Does this happen in RealmChangeListener? Because that should be impossible on UI thread unless you do it in onPostExeute

Yes that's happen on RealmChangeListener, in the method onChange, the listener is in onCreate method of activity. I get RealmList not consistent, I get in object's field RealmList elements from other RealList that are in realmbase. If it's form help I can send link from the files.

@ileste your problem doesn't seem to be (entirely?) related to this issue. You should probably create a new issue explaining it.

I have similar problem, I suppose, like @hardysim

You need to keep a strong reference to the RealmList.

Without code, I can't tell if you're messing something up, otherwise.

It should be impossible that the RealmList is inconsistent in RealmChangeListener. Notifiers are called only when the queries are all refreshed, I think? Although of course, object-store can verify this.

If you can provide a reproducible sample, that is great!

I need put let say like the example, in Bar class @LinkingObjects("bars") private final RealmResults foos = null; and everything is ok, good

I want to warm up this a bit because I just realized that my latest update on realm broke this for me (we're not sure why it worked for me but it did). Now, it behaves just like documented:

If a field points to another RealmObject this listener will only be triggered if the field is set to a new object or null. Updating the referenced RealmObject will not trigger this listener.


To sum it up (and get back to the original topic here):
I want to get notified, when a nested object changes. Is this possible?

we're not sure why it worked for me but it did

You probably were updating the parent object in the same transaction, without really thinking about it.

I want to get notified, when a _nested_ object changes. Is this possible?

My ugly solution is to update the parent object "manually". Usually, I have an updatedAt: Long property in the parent, which the child object is responsible to update whenever it is updated.

I've just realized that my new way of dealing with Realm + LiveData is a workaround (in some cases) as well:

I'm calling RealmChangeListener.onChange() manually in LiveData.onActive(). This way, my listener in Activity A gets notified when I update the model in Activity B and get back. See https://stackoverflow.com/a/53336629/2170109.

But this is just coincidence (because I get a notification on the parent-object and not the nested-one). Therefore, this is no solution to this issue here (but a workaround for my use case).

Was this page helpful?
0 / 5 - 0 ratings