Realm — It’s all about the choices we make

Viraj Tank
AndroidPub
Published in
7 min readSep 5, 2016

Realm is full of features and options, which means as a developer we have to make a choice based on our requirements. This post tries to document various choices in Realm and rationale behind selecting one.

Initialisation & Configuration

1. Default Realm vs inMemory Realm vs DynamicRealm

  1. If you want to use Realm as a database, you should use Default Realm, which is the default feature of Realm.
  2. If you want to use Realm as a cache library, you should use inMemory Realm, which can be done by adding .inMemory() in your RealmConfiguration and Realm will create an in memory storage which will work as a cache library. Note that all the data stored in inMemory Realm will be lost when you close Realm, and all the thread limitations also applies to inMemory Realm. inMemory Realm can also work in parallel to default Realm.
  3. If you can not or don’t want to define your model schema at compile time and need flexibility for model schema, DynamicRealm can be used for such cases to give you schema flexibility.

2. deleteRealmIfMigrationNeeded() vs migration()

  1. Upon schema change, if you don’t want any of the stored data in existing Realm database file, then use deleteRealmIfMigrationNeeded().
  2. Upon schema change, if you need stored data in exciting Realm database file, then you need to provide migration, using .migration().

3. Multiple Realm files vs RealmModules

  1. If you work with multiple users and you want to store the data between logins and logouts, you can create a dedicated Realm database file for each user, which can be handled using a custom RealmManager, which can manage Realm instances for each user.
  2. However, If you want to restrict access of specific model(s) to specific user(s), RealmModule(s) can be really useful in this and other such use cases. By default, a Realm database file knows about all the model classes that are RealmObjects. Using RealmModule(s) you can create sub group(s) of model(s) which can be visible to specific user(s).

4. Realm init & loading at App launching vs When first needed

  1. Realm init and loading is really fast, like all other Realm operations, but if you have preloaded data, it might slow down the loading process, which is why it would be a better practice to init and load Realm as part of the Splash screen.
  2. The other option is to init and load Realm when you need it for the first time. It would not be a good choice, since it might add waiting time for user when he is using the app, depending on the size of the database.

5. Realm.getDefaultInstance() vs Realm.getInstance(realmConfig)

  1. Creating a single RealmConfiguration and setting it as a default configuration and using Realm.getDefaultInstance() when needed, would be a better practice to follow, because a single definition of RealmConfiguration would be much easier to debug and manage.
  2. On the the other hand, if you have multiple definitions of the same RealmConfiguration in different parts of the application, it could become difficult to mange when modifications would be needed.

6. Multiple APK files vs Single APK file for playStore

Realm adds ~1750 method counts or in other words ~800 Kilobytes to your application, which makes it a mid sized library. There are 2 reasons behind the size of the library,

  1. Its full of features.
  2. Realm uses a common C++ engine, which is ported on multiple platforms like Android, iOS Objective-C, iOS Swift, Xamarin and React Native. In Android, Realm’s C++ engine is ported using NDK via a JNI layer, which means Realm must provide multiple .so (Shared library) for arm, mips and x86 platforms, which increases the size of overall Realm.

Size of the APK file can be reduced,

  1. If size of the APK you upload to playStore is a concern, it can be reduced if you build multiple APK files for different platforms like arm, mips and x86. This can easily be done with gradle configuration and it would reduce the size of the APK you upload to playStore.
  2. If size of the APK you upload to playStore is not a concern, you can use a singe APK file. When user installs the APK, only the relevant .so file will be used as per the user’s device, making the installable APK size lesser than the playStore APK size.

7. Safe vs Deep integration

  1. If you don’t need Realm’s auto update feature, use Safe integration, explained here: https://medium.com/@Viraj.Tank/realm-integration-in-android-best-practices-449919d25f2f
  2. If you want to use Realm’s auto update feature, use Deep integration, explained here: https://medium.com/@Viraj.Tank/deep-integration-of-realm-in-android-production-code-part-2-with-mvp-4cf44ab6289d

Write operations

8. copyTo* vs insert* methods

  1. until Realm version 1.1.0, Realm had copyTo* methods for write operations, which were not optimised.
  2. With version 1.1.0, Realm also provides insert* methods for write operations, which are optimised and much faster than copyTo* methods, so it would be the best practice to use insert* methods.

9. Transaction vs executeTransaction

  1. In Realm, all write operations must be protected by a transaction, as shown in the code above. But the problem here is it’s a lot of boilerplate code and if something goes wrong in write transaction, we are still committing the transaction.

2. On the other hand, if we use executeTransaction with lambda, it’s less code, and if something goes wrong, Realm will perform realm.cancelTransaction(), which is much safer.

10. Storing individual elements vs Storing the list

  1. This approach is a bit slower, since we are using multiple transactions, Hence avoid using it. (Note: If you add/edit multiple elements in a single event, your RealmChangeListeners will get triggered only once, on the next loop of Looper.)

2. A better approach would be to use one transaction block and storing the whole list in a single shot.

11. Write operations on mainThread vs on computationThread

  1. With Realm’s *Async methods, it is easier to perform write operations on main thread as well. Technically, nothing wrong in using this approach, it’s just that it breaks “Clean Architecture” and might be difficult to test.
  2. A simple rule of thumb for write transactions in Realm is to open Realm instance, perform write/edit/delete operation(s) on computationThread without *Async methods, and close Realm instance, for cleaner and maintainable code.

12. Read-Write Infinite Loop

In Realm, Edit operation is a combination of Read+Write. If you query data as observable by accident, note that those queries by default comes with a changeListener. This means, if you edit a live realmResult within the subscription, it could lead to an infinite loop, like given below, which could also happen if you query the results and add a change listener on queried data.

Instead, when you want to edit, make sure you don’t have a change listener added on queried data. A sample example is given below, note that the query is made without *Async method.

Read operations

13. addChangeListener vs asObservable

In Realm all the queried results are LIVE objects as long as you don’t make an in memory copy and access them on a looper thread. This means if the results get updated, Realm will send a change notification to all the looper threads. To achieve this behaviour Realm provides 2 options.

  1. First you can either query a result, and use .addChangeListener on top of the results, which will be notified when data changes.
  2. Or you can make a query in Realm and use .asObservable() for the results, it not only returns the results as RxJava observable, but it also creates a hot observable which internally adds a changeListener and calls .onNext() method when data changes.

14. LIVE data vs copyFromRealm

Realm, RealmObject and RealmResults can not be passed across threads, is the root cause behind majority of the issues reported on StackOverflow.

But why is it a limitation?

Because in Realm a Write operation only blocks other Write operations, and allows any number of parallel Read operations. This means Realm has to ensure consistency across all the data that is queried.

To do this Realm by default does not make an in memory copy for the data we query, instead it returns a pointer to retrieve those data when needed. This makes it easier for threads to get updated data without making a new query. All Realm needs to do is send a change notification.

  1. If you need this auto update feature, you should use LIVE data, and whenever you need to transfer them across threads, pass the id and query again on the other thread.
  2. If you don’t need auto update feature, you can make an in memory copy of the data you query using copyFromRealm() method and after that you can pass this copied data across threads.

15. Read operations on mainThread vs on computationThread

  1. It is advised that you perform all read operations needed for view loading on mainThread using *Async methods of Realm.
  2. If you need to query Realm for other reasons like, when need to edit data or want to check availability of data in Realm, it would be ideal to perform those read operations on computationThread without *Async methods.

Complete source code

Hope it helped you make an educated choice, with your Realm development. Complete source code is available on GitHub.

--

--

Viraj Tank
AndroidPub

Android developer & Mobile lead @Sociomantic Labs.