Always use Transaction blocks in Realm Android

Jitendra Alekar
3 min readDec 1, 2016

If you are new to Realm then this one choice could make your experience with this easy to use library could become much more pleasant.

Firstly, lets look at transactions. Read and write access in realm is ACID. This is possible because every write operation is a transaction. Although you can read any time the changes made to the data will be reflected only if you commit your transaction. Lets see an example.

Realm realm = Realm.getDefaultInstance();
try{

realm.beginTransaction();
....
// getToken() can throw exception
Token token = api.getToken();
Token myToken = realm.createObject(Token.class);
myToken.setTime(getCurrentTime());
//add more stuff
realm.copyToRealmOrUpdate(myToken);
realm.commitTransaction();}catch(..){ //print log
}

The above code seems fine at first. But things start to look ugly when an exceptions occurs. Lets say you show a snackbar with a retry option to allow you to get a valid token after an exception is caught. In such case you might get an illegalStateException saying that The Realm is already in a write transaction. What happened here, what did we miss. You might have guessed it already, we started a write transaction using beginTransaction(), but due to the exception it was not ended. So we need a way to cancel write in case when write wont be completed successfully. For such scenario we have the ability to cancel the transaction.

realm.beginTransaction();
....
//if something goes wrong cancel
//can go in catch block
realm.cancelTransaction()
//if everything is fine
realm.commitTransaction();

But don’t you think transaction vaguely relates to an atomic block. A block committed as a whole. The previous code can be neatly, efficiently and safely re-written using transaction blocks provided in realm.

realm.executeTransaction(new Realm.Transaction() {
@Override
public void execute(Realm realm) {
// getToken() can throw exception
Token token = api.getToken();
Token myToken = realm.createObject(Token.class);
myToken.setTime(getCurrentTime());
//add more stuff
realm.copyToRealmOrUpdate(myToken);
}
});

I promised you this would be neat, efficient and safe. Neatness depends on persons point of view. I do like writing code in such blocks, but that’s just a personal choice. But the other two promises still hold true. Transaction blocks also have asynchronous variants which can be used to put this mumbo-jumbo on a background thread and get callback when its done.

// Asynchronously update objects on a background thread
realm.executeTransactionAsync(new Realm.Transaction() {
@Override
public void execute(Realm realm) {
// getToken() can throw exception
Token token = api.getToken();
Token myToken = realm.createObject(Token.class);
myToken.setTime(getCurrentTime());
//add more stuff
realm.copyToRealmOrUpdate(myToken);
}
}, new Realm.Transaction.OnSuccess() {
@Override
public void onSuccess() {
//Cheers! some worker thread did it for us!
}
});

This is safer too as even if an exception occurs or the block does not complete its complete execution for some reason you dont have to worry about cancelling the transaction or seeing this guy trashing your logcat.

Always close your realm instance. In fact realm instances are reference counted. What that means is that call close as much times as you call getInstance().

Realm realm = Realm.getDefaultInstance();
try{
// Asynchronously update objects on a background thread
realm.executeTransactionAsync(new Realm.Transaction() {
@Override
public void execute(Realm realm) {
// getToken() can throw exception
Token token = api.getToken();
Token myToken = realm.createObject(Token.class);
myToken.setTime(getCurrentTime());
//add more stuff
realm.copyToRealmOrUpdate(myToken);
}
}, new Realm.Transaction.OnSuccess() {
@Override
public void onSuccess() {
//Cheers! some worker thread did it for us!
}
});
}
catch(...){...}
finally{
//must close realm, as its reference counted.
realm.close();
}

So finally{…} Lets draw this to a close(). “pun intended”

--

--