MongoDB Developer
Realm
plus
Sign in to follow topics
MongoDB Developer Centerchevron-right
Developer Topicschevron-right
Productschevron-right
Realmchevron-right

Migrating Android Apps from Realm Java SDK to Kotlin SDK

Jorge D. Ortiz-Fuentes, Mohit Sharma10 min read • Published Mar 28, 2022 • Updated Sep 05, 2023
RealmKotlin
Facebook Icontwitter iconlinkedin icon
Realm logo
Rate this article
star-empty
star-empty
star-empty
star-empty
star-empty

Introduction

So, it's here! The engineering team has released a major milestone of the Kotlin SDK. The preview is available to you to try it and make comments and suggestions.
Until now, if you were using Realm in Android, you were using the Java version of the SDK. The purpose of the Realm Kotlin SDK is to be the evolution of the Java one and eventually replace it. So, you might be wondering if and when you should migrate to it. But even more important for your team and your app is what it provides that the Java SDK doesn't. The Kotlin SDK has been written from scratch to combine what the engineering team has learned through years of SDK development, with the expressivity and fluency of the Kotlin language. They have been successful at that and the resulting SDK provides a first-class experience that I would summarize in the following points:
  • The Kotlin SDK allows you to use expressions that are Kotlin idiomatic—i.e., more natural to the language.
  • It uses Kotlin coroutines and flows to make concurrency easier and more efficient.
  • It has been designed and developed with Kotlin Multiplatform in mind.
  • It has removed the thread-confinement restriction on Java and it directly integrates with the Android lifecycle hooks so the developer doesn't have to spin up and tear down a realm instance on every activity lifecycle.
  • It's the way forward. MongoDB is not discontinuing the Java SDK anytime soon, but Kotlin provides the engineering team more resources to implement cooler things going forward. A few of them have been implemented already. Why wouldn't you want to benefit from them?
Are you on board? I hope you are, because through the rest of this article, I'm going to tell you how to upgrade your projects to use the Realm Kotlin SDK and take advantage of those benefits that I have just mentioned and some more. You can also find a complete code example in this repo.
Build better mobile apps with Atlas Device Sync: Atlas Device Sync is a fully-managed mobile backend-as-a-service. Leverage out-of-the-box infrastructure, data synchronization capabilities, built-in network handling, and much more to quickly launch enterprise-grade mobile apps. Get started now by build: Deploy Sample for Free!

Gradle build files

First things first. You need to make some changes to your build.gradle files to get access to the Realm Kotlin SDK within your project, instead of the Realm Java SDK that you were using. The Realm Kotlin SDK uses a gradle plugin that has been published in the Gradle Plugin Portal, so the prefered way to add it to your project is using the plugins section of the build configuration of the module —i.e., app/build.gradle— instead of the legacy method of declaring the dependency in the buildscript block of the project build.gradle.
After replacing the plugin in the module configuration with the Kotlin SDK one, you need to add an implementation dependency to your module. If you want to use Sync with your MongoDB cluster, then you should use 'io.realm.kotlin:library-sync', but if you just want to have local persistence, then 'io.realm.kotlin:library-base' should be enough. Also, it's no longer needed to have a realm dsl section in the android block to enable sync.
build.gradle
Comparison
Java SDK
Kotlin SDK
app/build.gradle
Comparison
Java SDK
Kotlin SDK
If you have more than one module in your project and want to pin the version number of the plugin for all of them, you can define the plugin in the project build.gradle with the desired version and the attribute apply false. Then, the build.gradle files of the modules should use the same plugin id, but without the version attribute.
Multimodule Configuration
Project build.gradle
Modules build.gradle

Model classes

Kotlin scope functions (i.e., apply, run, with, let, and also) make object creation and manipulation easier. That was already available when using the Java SDK from kotlin, because they are provided by the Kotlin language itself.
Defining a model class is even easier with the Realm Kotlin SDK. You are not required to make the model class open anymore. The Java SDK was using the Kotlin Annotation Processing Tool to derive proxy classes that took care of interacting with the persistence. Instead, the Kotlin SDK uses the RealmObject interface as a marker for the plugin. In the construction process, the plugin identifies the objects that are implementing the marker interface and injects the required functionality to interact with the persistence. So, that's another change that you have to put in place: instead of making your model classes extend —i.e., inherit— from RealmObject, you just have to implement the interface with the same name. In practical terms, this means using RealmObject in the class declaration without parentheses.
Model Class Definition
Java SDK
Kotlin SDK
There are also changes in terms of the type system. RealmList that was used in the Java SDK to model one-to-many relationships is extended in the Kotlin SDK to benefit from the typesystem and allow expressing nullables in those relationships. So, now you can go beyond RealmList<String> and use RealmList<String?>. You will get all the benefits of the syntax sugar to mean that the strings the object is related to, might be null. You can check this and the rest of the supported types in the documentation of the Realm Kotlin SDK.

Opening (and closing) the realm

Using Realm is even easier now. The explicit initialization of the library that was required by the Realm Java SDK is not needed for the Realm Kotlin SDK. You did that invoking Realm.init() explicitly. And in order to ensure that you did that once and at the beginning of the execution of your app, you normally put that line of code in the onCreate() method of the Application subclass. You can forget about that chore for good.
The configuration of the Realm in the Kotlin SDK requires passing the list of object model classes that conform the schema, so the builder() static method has that as the argument. The Realm Kotlin SDK also allows setting the logging level per configuration, should you use more than one. The rest of the configuration options remain the same.
It's also different the way you get an instance of a Realm when you have defined the configuration that you want to use. With the Java SDK, you had to get access to a thread singleton using one of the static methods Realm.getInstance() or Realm.getDefaultInstance() (the latter when a default configuration was being set and used). In most cases, that instance was used and released, by invoking its close() method, at the end of the Activity/Fragment lifecycle. The Kotlin SDK allows you to use the static method open() to get a single instance of a Realm per configuration. Then you can inject it and use it everywhere you need it. This change takes the burden of Realm lifecycle management off from the shoulders of the developer. That is huge! Lifecycle management is often painful and sometimes difficult to get right.
Realm SDK Initialization
Java SDK
Kotlin SDK
Objects in the Realm Kotlin SDK are now frozen to directly integrate seamlessly into Kotlin coroutine and flows. That means that they are not live as they used to be in the Realm Java SDK and don't update themselves when they get changed in some other part of the application or even in the cloud. Instead, you have to modify them within a write transaction, i.e., within a write or writeBlocking block. When the scope of the block ends, the objects are frozen again.
Even better, the realms aren't confined to a thread. No more thread singletons. Instead, realms are thread-safe, so they can safely be shared between threads. That means that you don't need to be opening and closing realms for the purpose of using them within a thread. Get your Realm and use it everywhere in your app. Say goodbye to all those lifecycle management operations for the realms!
Finally, if you are injecting dependencies of your application, with the Realm Kotlin SDK, you can have a singleton for the Realm and let the dependency injection framework do its magic and inject it in every view-model. That's much easier and more efficient than having to create one each time —using a factory, for example— and ensuring that the close method was called wherever it was injected.

Writing data

It took a while, but Kotlin brought coroutines to Android and we have learned to use them and enjoy how much easier they make doing asynchronous things. Now, it seems that coroutines are the way to do those things and we would like to use them to deal with operations that might affect the performance of our apps, such as dealing with the persistence of our data.
Support for coroutines and flows is built-in in the Realm Kotlin SDK as a first-class citizen of the API. You no longer need to insert write operations in suspending functions to benefit from coroutines. The write {} method of a realm is a suspending method itself and can only be invoked from within a coroutine context. No worries here, since the compiler will complain if you try to do it outside of a context. But with no extra effort on your side, you will be performing all those expensive IO operations asynchronously. Ain't that nice?
You can still use the writeBlocking {} of a realm, if you need to perform a synchronous operation. But, beware that, as the name states, the operation will block the current thread. Android might not be very forgiving if you block the main thread for a few seconds, and it'll present the user with the undesirable "Application Not Responding" dialog. Please, be mindful and use this only when you know it is safe.
Another additional advantage of the Realm Kotlin SDK is that, thanks to having the objects frozen in the realm, we can make asynchronous transactions easier. In the Java SDK, we had to find again the object we wanted to modify inside of the transaction block, so it was obtained from the realm that we were using on that thread. The Kotlin SDK makes that much simpler by using findLatest() with that object to get its instance in the mutable realm and then apply the changes to it.
Asynchronous Transaction Comparison
Java SDK
Kotlin SDK

Queries and listening to updates

One thing where Realm shines is when you have to retrieve information from it. Data is obtained concatenating three operations:
  1. Creating a RealmQuery for the object class that you are interested in.
  2. Optionally adding constraints to that query, like expected values or acceptable ranges for some attributes.
  3. Executing the query to get the results from the realm. Those results can be actual objects from the realm, or aggregations of them, like the number of matches in the realm that you get when you use count().
The Realm Kotlin SDK offers you a new query system where each of those steps has been simplified.
The queries in the Realm Java SDK used filters on the collections returned by the where method. The Kotlin SDK offers the query method instead. This method takes a type parameter using generics, instead of the explicit type parameter taken as an argument of where method. That is easier to read and to write.
The constraints that allow you to narrow down the query to the results you care about are implemented using a predicate as the optional argument of the query() method. That predicate can have multiple constraints concatenated with logical operators like AND or OR and even subqueries that are a mayor superpower that will boost your ability to query the data.
Finally, you will execute the query to get the data. In most cases, you will want that to happen in the background so you are not blocking the main thread. If you also want to be aware of changes on the results of the query, not just the initial results, it's better to get a flow. That required two steps in the Java SDK. First, you had to use findAllAsync() on the query, to get it to work in the background, and then convert the results into a flow with the toFlow() method. The new system simplifies things greatly, providing you with the asFlow() method that is a suspending function of the query. There is no other step. Coroutines and flows are built-in from the beginning in the new query system.
Query Comparison
Java SDK
Kotlin SDK
As it was the case when writing to the Realm, you can also use blocking operations when you need them, invoking find() on the query. And also in this case, use it only when you know it is safe.

Conclusion

You're probably not reading this, because if I were you, I would be creating a branch in my project and trying the Realm Kotlin SDK already and benefiting from all these wonderful changes. But just in case you are, let me summarize the most relevant changes that the Realm Kotlin SDK provides you with:
  • The configuration of your project to use the Realm Kotlin SDK is easier, uses more up-to-date mechanisms, and is more explicit.
  • Model classes are simpler to define and more idiomatic.
  • Working with the realm is much simpler because it requires less ceremonial steps that you have to worry about and plays better with coroutines.
  • Working with the objects is easier even when doing things asynchronously, because they're frozen, and that helps you to do things safely.
  • Querying is enhanced with simpler syntax, predicates, and suspending functions and even flows.
Time to code!

Facebook Icontwitter iconlinkedin icon
Rate this article
star-empty
star-empty
star-empty
star-empty
star-empty
Related
Tutorial

Turning Your Local Game into an Online Experience with MongoDB Realm Sync


Mar 06, 2023 | 12 min read
Tutorial

Build Your First iOS Mobile App Using Realm, SwiftUI, & Combine


Mar 06, 2023 | 14 min read
Tutorial

Build an Infinite Runner Game with Unity and the Realm Unity SDK


Feb 03, 2023 | 12 min read
Article

Announcing the GA of the Realm Flutter SDK


Jun 14, 2023 | 6 min read
Table of Contents