In an asynchronous transaction, there is no way to return a result that was computed during the transaction back to the main thread.
Something like that would be nice:
final String nameThatMightExistsOrNot = "test";
realm.executeTransactionAsync(new Realm.Transaction<Long>() { // typed callback listener
@Override
public Long execute(Realm bgRealm) {
User user = realm.where(User.class).equalTo("name", nameThatMightExistsOrNot).findFirst();
if (user == null) {
user = bgRealm.createObject(User.class, generateId());
}
user.setName(nameThatMightExistsOrNot);
return user.getId();
}
@Override
public void onSuccess(Long userId) {
// Now we have the userId!
}
}
The thing is that currently there doesn't seem to be any async way to retrieve data, update the Realm depending on that data, and return the result back to the main thread. The problem with doing the check before outside the transaction and save it for later is that there might be another transaction that changes the result of the check.
Copy+paste from documentation:
realm.executeTransactionAsync(new Realm.Transaction() {
@Override
public void execute(Realm bgRealm) {
User user = realm.where(User.class).equalTo("name", nameThatMightExistsOrNot).findFirst();
if (user == null) {
user = bgRealm.createObject(User.class);
}
user.setName(nameThatMightExistsOrNot);
}
}, new Realm.Transaction.OnSuccess() {
@Override
public void onSuccess() {
// Here I have no idea what happened. Maybe I need to run some logic
// only when the user was added, but I couldn't possibly know.
}
});
Realm version(s): 4.1.0
Realm sync feature enabled: no
Android Studio version: latest
You can use something (slightly tacky) like AtomicReference<T>. declared before the executeTransactionAsync
Sounds good but couldn't find any documentation about it. Got an example?
AtomicReference<String> nameRef = new AtomicReference<>();
realm.executeTransactionAsync(new Realm.Transaction() {
@Override
public void execute(Realm bgRealm) {
User user = realm.where(User.class).equalTo("name", nameThatMightExistsOrNot).findFirst();
if (user == null) {
user = bgRealm.createObject(User.class);
}
user.setName(nameThatMightExistsOrNot);
nameRef.set(nameThatMightExistsOrNot);
}
}, new Realm.Transaction.OnSuccess() {
@Override
public void onSuccess() {
// Here I have no idea what happened. Maybe I need to run some logic
// only when the user was added, but I couldn't possibly know.
String name = nameRef.get();
}
});
Thanks, this seems to work! Wrapped the whole thing into my own class, now I can do as I suggested.
Realm team, feel free to close, would be nice having this in the API directly though. What would be even more awesome if when returning a RealmObject in the transaction thread, you would automatically refetch it for me in the callback on the main thread. Also, not sure why you need three separate objects instead of just one with three methods for transaction, success and error. Something like that would be really cool:
realm.executeTransactionAsync(new Realm.TransactionWithResult<User>() {
@Override
public User execute(Realm bgRealm) {
User user = bgRealm.where(User.class).equalTo("name", name).findFirst();
if (user == null) {
user = bgRealm.createObject(User.class);
}
user.setName(name);
return user; // user on background thread
}
@Override
public void onSuccess(User user) {
// user here is on from ui thread
}
@Override
public void onError(Throwable error) {
// treat error here
}
});
I do admit that the introduction of the ThreadSafeReference which would allow passing RealmObject between thread would be nice, but due to how Java's GC is kinda random, that hasn't been added (and why Realm has .close() methods instead).
By the way, an interesting fact - I actually didn't come up with this completely on my own, I saw the following (currently unused) code in the Kotlin extensions library:
inline fun <T> Realm.callTransaction(crossinline action: Realm.() -> T): T {
val ref = AtomicReference<T>()
executeTransaction {
ref.set(action(it))
}
return ref.get()
}
Which basically does what we just did here, except only with synchronous transaction.
Yes, unfortunately there is a very big difference between async and synchronous transactions, so doing it for asynchronous transactions is a lot more involved.
Well what I'm doing now is basically passing the primary key back through the AtomicReference and re-fetching the object on the main thread. Is there no way to do this automatically?
You are doing it correctly with current api
Yes, but having Realm doing it for me would be much more convenient. :)
@freezy we have an issue to track this, see https://github.com/realm/realm-java/issues/1208
Most helpful comment