Search code examples
javaandroidrealm

Deeper understanding about how Realm works?


Q1 : Please let me know what is different between two ways of implementation in below (about get realm instance). I want to know which one is faster, lighter in memory and what is recommended ?

1. Set and Get Realm as Default (with specific config)

private void setupCustomRealm() {
        if (!Utils.isStringHasText(databaseName)) {
            databaseName = DbManager.getInstance().getCurrentDb();
        }
        // get config
        RealmConfiguration config = getRealmConfigByDBName(databaseName);
        Realm.setDefaultConfiguration(config);
        Realm.compactRealm(config);

    }

public Realm getCustomRealm() {
      if (firstTime) {
             setupCustomRealm();
      }
      return Realm.getDefaultInstance();
}

2 .Get Realm from config directly

public Realm getCustomRealm(Context context) {
        if (!Utils.isStringHasText(databaseName)) {
            databaseName = DbManager.getInstance().getCurrentDb();
        }
        // get config
        RealmConfiguration config = getRealmConfigByDBName(context, databaseName);
        Realm.compactRealm(config);
        return Realm.getInstance(config);
    }

Q2 : In my application, now we are consider between 2 ways of implementation.

1: We create a new Realm instance every time when we need to do something with Database (in both of worker thread and UI thread) and close it when task get done.

2: We create only one instance of Realm and let it live along with application, when quit application we close instance above.

Please explain me the advantage and disadvantage of each one and which ways is recommended (My application using Service to handle database and network connection)

If I have 2 heavy tasks (take a long time to complete it's transaction), what is difference between execute 2 task by one Realm instance and execute 2 task on 2 Realm instances in 2 separate thread (I mean one thread have one Realm instances and instance will executes one in 2 tasks above), and which one if safer and faster.

what will happen if there is a problem while executing a transaction (example not responding or throws some exception)


Solution

  • Note: I am not an official Realm person, but I've been using Realm for a while now.

    Here's a TL;DR version


    1.) It's worth noting a few things:

    • A given Realm file should be accessed only with the same RealmConfiguration throughout the application, so the first solution here is preferable (don't create a new config for each Realm).
    • Realm.compactRealm(RealmConfig) works only when there are no open Realm instances on any threads. So either at application start, or at application finish (personally I found that it makes start-up slower, so I call compactRealm() when my activity count reaches 0, which I manage with a retained fragment bound to the activity - but that's just me).

    2.) It's worth noting that Realm.getInstance() on first call creates a thread-local cache (the cache is shared among Realm instances that belong to the same thread, and increments a counter to indicate how many Realm instances are open on that given thread. When that counter reaches 0 as a result of calling realm.close() on all instances, the cache is cleared.

    It's also worth noting that the Realm instance is thread-confined, so you will need to open a new Realm on any thread where you use it. This means that if you're using it in an IntentService, you'll need to open a new Realm (because it's in a background thread).

    It is extremely important to call realm.close() on Realm instances that are opened on background threads.

    Realm realm = null;
    try {
        realm = Realm.getDefaultInstance();
        //do database operations
    } finally {
        if(realm != null) {
            realm.close();
        }
    }
    

    Or API 19+:

    try(Realm realm = Realm.getDefaultInstance()) {
        //do database operations
    }
    
    • When you call realm.close() on a particular Realm instance, it invalidates the results and objects that belong to it. So it makes sense both to open/close Realms in Activity onCreate() and onDestroy(), or open it within the Application and share the same UI thread Realm instance for queries on the UI thread.

    • (It's not as important to close the Realm instance on the UI thread unless you intend to compact it after all of them are closed, but you have to close Realm instances on background threads.)

    Note: calling RealmConfiguration realmConfig = new RealmConfiguration.Builder(appContext).build() can fail on some devices if you call it in Application.onCreate(), because getFilesDir() can return null, so it's better to initialize your RealmConfiguration only after the first activity has started.

    With all that in mind, the answer to 2) is:

    • While I personally create a single instance of Realm for the UI thread, you'll still need to open (and close!) a new Realm instance for any background threads.

    • I use a single instance of Realm for the UI thread because it's easier to inject that way, and also because executeTransactionAsync()'s RealmAsyncTask gets cancelled if the underlying Realm instance is closed while it's still executing, so I didn't really want that to happen. :)

    • Don't forget, that you need a Realm instance on the UI thread to show RealmResults<T> from realm queries (unless you intend to use copyFromRealm() which makes everything use more memory and is generally slower)

    • IntentService works like a normal background thread, so you should also close the realm instance there as well.

    Both heavy tasks work whether it's the same Realm instance or the other (just make sure you have a Realm instance on that given thread), but I'd recommend executing these tasks serially, one after the other.

    If there's an exception during the transaction, you should call realm.cancelTransaction() (the docs say begin/commit, but it always forgets about cancel).

    If you don't want to manually manage begin/commit/cancel, you should use realm.executeTransaction(new Realm.Transaction() { ... });, because it automatically calls begin/commit/cancel for you. Personally I use executeTransaction() everywhere because it's convenient.