In Windows and Mac OS X with Android Studio 2.1
Using Realm version 0.91 (got it with 0.90.1 too), and Kotlin in the project, I'm having a lot of trouble with the onChangeListener. Here's a sample MyApplication.zip
What my sample does : a main activity will call a fake webservice that have a list of animals (100 but can be changed). It calls every second for 20 more animal, and then it is added in realm. When onChange() is called, we display all the name of the animals that we have in the database (from 0 to max nb of animal).
But here the main problem : although it sometimes work (but I have no clue why), most of the time he displays nothing because onChange() is never called. I tried with an anonymous lambda, then with the mainActivity has listener.
Sometime I get the first or the second call to display, but then nothing.
I would like for you to try the sample if you get the same problem.
If you can't reproduce it, can you turn on instant run, and in gradles.properties, make kotlin.incremental=true ?
I'll try the same sample in Java, but which component is not working as supposed.
Ok same issue with Java :
MyApplicationjava.zip
It should display from 0 to 199, and always stop at 79 or 99.
I checked something : a OnChangeListener on Realm (not a RealmResult) is called each time correctly, it's the listener of the RealmResult that is not.
Seems linked to #2569 and #1894
I can also confirm that it probably comes from a GC pass, using the memory graph of the app, that onChange() stop being called just after GC .
EDIT : I found a temporary fix =>
i was using :
fun... {
val query = realm.where(Animal::class.java).findAll()
query.addChangeListener {
val str = displayListOfAnimals(it)
Timber.d("list animal received from Realm : {${str}}")
textv.text = str
}
}
But putting query has an attribute :
lateinit var query: RealmResult<Animal>
fun ... {
query = realm.where(Animal::class.java).findAll()
query.addChangeListener {
val str = displayListOfAnimals(it)
Timber.d("list animal received from Realm : {${str}}")
textv.text = str
}
}
fixes the issue !
The listener is anonymous here, I tried to keep the listener in an attribute but that didn't work too (like in my samples). Keeping the query does work though.
I'm eager to be Monday to have more info on this.
Hi @NitroG42
I tried to play around with your sample, and you are indeed correct. It seems that moving the Listener from an attribute to being anonymous makes a difference. That is really strange. Right now I am a bit uncertain if this is due to Kotlin doing something wrong or us. I'll look into it and report back.
Hi @cmelchior really glad you could reproduce the issue !
Just to avoid any confusion :
Hi @NitroG42
I found the problem. You are saving your RealmResult in a local variable in the onCreate method. This means that it is eligible for being GC'ed as soon as you exit the onCreate method, and when that happens the ChangeListener is no longer triggered. This is the intended behavior.
If you save you query in a field instead, it will work correctly.
Hey @cmelchior, that is what I found too, like I said, after some though that seems normal, but looking at the doc, there is nothing about it. Basically, I didn't expected that I needed to keep the RealmResult, because I was keeping the listener, and the onChange() method pass the RealmResult as parameter. Maybe there should be a kind of "Memory Management" segment in the doc ?
Thank you anyway for looking at this.
I think we need to add a section in https://realm.io/docs/java/latest/#asynchronous-queries to warn the users about the GC behaviour. This question has come a couple of times.
Just spent a couple of hours trying to figure out what was going on. Please add it to the documentation.
Same. In most of the cases I was able to get results using an async query, but sometimes I wasn't getting a result without any exceptions. RealmResults were storing as a local variable. Moving them to the class variable seem to fix the issue.
awesome! Moving from local variable to class variable solved problem for me
I agree, in the documentation, the "asynchronous queries" block's Register a callback is wrong; it does not store the results as field variable.
We updated all Javadoc for changelisteners to explicitly mention that you need to hold a strong reference to the object. Closing
Most helpful comment
Hi @NitroG42
I found the problem. You are saving your RealmResult in a local variable in the
onCreatemethod. This means that it is eligible for being GC'ed as soon as you exit theonCreatemethod, and when that happens the ChangeListener is no longer triggered. This is the intended behavior.If you save you query in a field instead, it will work correctly.