Realm-java: Realm on async thread not notify instances of Realm on main looper

Created on 25 Jan 2017  路  23Comments  路  Source: realm/realm-java

I have an application that updates a value of a determined model. Imagine this code, this is very simple:

This code runs very fast, I think it is a problem of synchronisation.

override fun runAsync(id : Int, duration: Long) { // Duration is 1000
    try {

      if (id > -1) {
        throw IllegalStateException("id is invalid, impossible to update duration")
      }

      //INVALID_VALUE is -1
      if (duration <= INVALID_VALUE) {
        throw IllegalStateException("Duration is a invalid value, aborting")
      }

      val realm = Realm.getDefaultInstance()
      realm.executeTransaction {
        val model = realm.where(Model::class.java)
            ?.equalTo("id", id)
            ?.findFirst()

        model?.let { model ->
          if (model.layers.isNotEmpty) { //Always true
            model.layers.last().duration = duration
          }
        }

      }
      realm.close()

      //Realm used just to assert that the update is correct.
      val tempRealm = Realm.getDefaultInstance()
      val model = tempRealm.where(Model::class.java)
          ?.equalTo("id", id)
          ?.findFirst()

      model?.layers?.last()?.let {
        println("Value updated: ${it.duration}") //Return 1000
      }

      tempRealm.close()

      //Notify the main thread to get the data
      notifyMainThread()

    } catch (e: Exception) {
      e.printStackTrace()
    }
  }

No exception was throw, no error. Everything is ok.

After the app return to the main looper, I did query for this Model again, using the same id.

//After return to the main looper
fun notifyMainThread() {
  val realm = Realm.getDefaultInstance()
  val model = tempRealm.where(Model::class.java)
          ?.equalTo("id", id)
          ?.findFirst()

      model?.layers?.last()?.let {
        println("Value updated: ${it.duration}") //Return INVALID_VALUE
      }
}

I added a RealmChangeListener and it solved the problem, but I think that is not correct, I am using a async task to avoid any kinda of problem on inserting and updating. Is there any way to force the async task to be locked until the notify of data changed on other Realm?

Realm: 2.3.0
Kotlin: 1.0.4

Thank you.

Most helpful comment

Technically it is not guaranteed that the Realm on the UI thread is immediately updated on the next event loop after an asynchronous write on another thread, so AsyncTask returns "too quickly" and the Realm isn't updated yet.

I have not figured out a solution to this problem, but maybe @cmelchior knows.

The recommendation is to use RealmChangeListener instead of direct UI-thread callbacks.

All 23 comments

@Zhuinden Sorry I missed to close the tempRealm instance on this sample, but the problem wasn't fixed.

Technically it is not guaranteed that the Realm on the UI thread is immediately updated on the next event loop after an asynchronous write on another thread, so AsyncTask returns "too quickly" and the Realm isn't updated yet.

I have not figured out a solution to this problem, but maybe @cmelchior knows.

The recommendation is to use RealmChangeListener instead of direct UI-thread callbacks.

@Zhuinden Sometimes this value comes correct, so I added the listener if needed.

Make the method Realm.refresh() great again

Make AsyncTasks great again

@beeender By some Realm some instances of Realm are not being updated (more than a second of difference between the start of this Realm instantiation and the usage).
I would like to know if I can force the refresh of the Realm, not using a cache (of the method getDefaultInstance). By some reason, sometimes when I start a new Realm inside a async thread, this Realm is not synchronised with the latest Realm update. That is crazy...
So... I would use the method waitForChange() but I don't know if this Realm is waiting for changes or not.

I can't use the change listener, this is not the main looper...

@ppamorim this code refreshes the Realm on the current thread: http://stackoverflow.com/a/38839808/2413303

@Zhuinden Thank you! I will try it

@Zhuinden Unfortunately I can't use the variable realm.handlerController.

I really don't mind to recreate the instance of Realm, this is not a problem on this part of my source code. Any alternative?

You can, just pay attention to the package in that answer

I am using Gradle I don't know if I can modify it.

You just need to create a package in the root called io and inside it create a package called realm and from there you can create RealmRefresh which can access the handler controller

Very good way to "inject" code.

@Zhuinden You saved my life! Thank you very much!

Glad to hear it worked :)
I posted this issue https://github.com/realm/realm-java/issues/3476 about this a long time ago but alas

I'll close the issue since @Zhuinden's suggestion seems to help.

@kneth Do you think that's a correct way to fix this problem? I don't think so...

@ppamorim I can reopen the issue if the issue hasn't been solved (and the stackoverflow link wasn't of any help to you).

@kneth I believe that you don't need to do it. I will be waiting the rewrite of the async queries and the implementation of a proper way to sync the Realm.

You can use refresh() method available since Realm 3.2+

Was this page helpful?
0 / 5 - 0 ratings