Caching With Realm and RxJava

Including code examples and working project

Inspiration

I always liked apps what still work offline. Partly because I like use mobile operators with awful network coverage and partly because I run out of data frequently. That’s why I want to bring my future users the pleasure of slacking off in those situations where the network is bad.

Here comes the inspiration to build fully cached apps, and a way to do that is by having databases in our phones. SQLite got a lot of hate and Realm is becoming very popular, so let’s see how can we build something with it.

All the code from this article can be found here in a fully working project: https://github.com/miquelbeltran/android-rxjava-realm-cache

Technology

Two powerful libraries will help us: RxJava and Realm. We are going to use RxJava to combine the results from the API call with the cached results from the Realm database (in case they exists).

Supporting us there’s RetroFit and GSON to handle the REST communication. I will be using the Open Weather Map API: http://openweathermap.org/

Because Jack is not supported by Realm at this time. I will be using Retrolambda for cleaner code examples with function references.


Start without Realm

Before we jump into Realm, let’s take a look at our API call:

This is a simple API call using Retrofit + RxJava. getWeather returns a WeatherResponse observable object, a POJO generated from the JSON response.

We can implement the call by subscribing to the Observable<WeatherResponse> on the IO thread and observing the responses from the Android main thread.

The schedulers will play an important role in our solution with Realm.

This is how our current solution looks like. The data goes from the cloud to our display straight from the REST call.

Only REST call

Adding Realm

Now we are going to add Realm in the middle. The response will be stored into the database before it is displayed. Because how Realm works and we want our response object to be a “managed object” from Realm, we need to create a new WeatherRealm object within our Realm instance.

REST call with storage in the middle

The second time we query the data, we want to first obtain anything that is stored in the database and then obtain it fresh from the API.

Combining REST call response with stored values

Going back to the schedulers, we don’t want to write to the database in the main thread (blocking the UI) and we don’t want to use either the IO thread since it may be blocked already with another slow network call. We are going to use the Computation scheduler for that task.

Using different threads for different tasks

Coding the solution

We will need to implement two methods: Read from Realm and Write to Realm. But first need to declare our RealmObject.

We just want to store the city name and its temperature. We need to create a new object that extends a RealmObject and add our two properties.

Read from Realm

We want to read from Realm on the UI thread, so we need to keep a Realm instance for the UI thread as long as we want to use the WeatherRealm object.

This is why we get a Realm instance on onCreate and don’t forget to close it on onDestroy.

findInRealm contains the query we want to run to search for the object instance in the database. The findInRealm method will be used as well in the write method to update any existing instances, so we pass the Realm instance as a parameter rather than using realmUI.

realm.where(WeatherRealm.class).equalTo("name", name).findFirst();

From my WeatherRealms, give me the first one whose name is “Berlin”.

Because we declared “name” as PrimaryKey, it is both making it a unique key and optimizing the query search.

Write to Realm

For writing we get another Realm instance. We don’t want to use the realmUI instance from the main thread, because we will be on the computation threads from RxJava.

ExecuteTransaction is a clean way to perform write operations within a Realm instance. It will commit the transaction for us, and if it fails, the transaction is canceled automatically.

We use the findInRealm with our transtactionRealm instance, again, rather than using realmUI, because we won’t be able to use a RealmObject that belongs to a different thread.

If the object does not exist, create a new one with the name as the primary key. Now we update the temperature value.

When the transaction finishes, the changes are stored. Don’t forget to close the current Realm instance.

Finally we pass the name (primary key) to the next method in the chain.

Building the Observable

Now it’s time to put everything together and chain our read and write methods.

First, we create the Observable for the API response.

The service call does not change and remains the same.

I have added a 1 second delay to the Obsevable for demo purposes, so we will see later the difference between the two onNext events.

The first change is the write to realm operation:

// Write to Realm on Computation scheduler                .observeOn(Schedulers.computation())                .map(this::writeToRealm)

Here we are calling to our writeToRealm method passing the WeatherResponse from the API. The writeToRealm method returns the primary key of our updated WeatherRealm (the name). Important here also that we observe on the computation scheduler, so the write operation happens on that thread pool.

Secondly, we read the updated WeatherRealm, using the passed primary key, but from the main thread, because we want to use it in the UI.

// Read results in Android Main Thread (UI) .observeOn(AndroidSchedulers.mainThread()) .map(this::readFromRealm);

As you can see, we use observeOn rather than subscribeOn. That’s because we want our observer (the one that will update our TextViews) to be on the main thread, similar to the previous solution. By putting observeOn before the map call, the map call is executed on the main thread as well.

As you can see, we can call observeOn many times, and the next operations will be running (observing) on that thread. Instead, subscribeOn works upward and can only be set once. Learn more about observeOn on the excelent RxJava documentation http://reactivex.io/documentation/operators/observeon.html

readFromRealm takes the primary key and reads the WeatherRealm from the Realm instance that belongs to the main thread.

Combining with the cached WeatherRealm

// Read any cached results
WeatherRealm cachedWeather = readFromRealm(name);
if (cachedWeather != null)
// Merge with the observable from API
observable = observable
.mergeWith(Observable.just(cachedWeather));

mergeWith is the key part here. We create a simple Observable with the cachedWeather from the existing Realm database. mergeWith takes care of combining the two WeatherRealm Observables, the one from the API and the one from the database.


Running

When we subscribe to the resulting Observable, we obtain two onNext events. One from the cached database and one from the API.

If we print the two values in the log, we will see two entries: The first one immediately after we subscribe, because we obtain the stored result from Realm. The second one comes a second later from the API call.

09-25 19:11:59.022 ... MainActivity: City: Berlin, Temp: 18.22
09-25 19:12:00.275 ... MainActivity: City: Berlin, Temp: 18.22

If the user has a slow connection, they will immediately see the cached values on start, rather than just a spinning progress bar.

In case the user is fully offline, they will still see the cached values, and then we can use the onError event we get from Retrofit to display a sad face.


Thanks for getting this far! I hope you enjoyed this publication. If you think it was worth reading, please share it and more people will benefit from it. You can also click on the Recommend ❤ button if you are Medium user. And of course, feel free to leave your feedback!

You can follow my updates on Twitter and LinkedIn. And find more code examples in my GitHub profile.