Clean Android networking with local database offline caching app in 2019

Including Retrofit, Kotlin coroutines, MVVM, LiveData, Koin and ObjectBox

Have you ever had a situation when you used some deprecated code found on Stack Overflow or when you read some old blog post and you were not so sure if this knowledge still applies? Well, you are not alone! Due to endless technology changes, we as programmers have to adapt to this world of knowledge instability. And, especially, Android developers should know something about it - suffice it to say that Google has deprecated so many fundamental classes and functions in API 28 (Android 9.0) that learning its equivalents took a lot of our valuable time.

Okay, so what’s your point?

The goal of this blog post is to show some recent solutions that I’ve gathered while my research. I hope this information may be helpful for some people looking for easy and clean solutions to common, current Android dev problems. We all love simple and no-boilerplate code, after all!

Stop talking and show me your code!

Not so fast, unfortunately. Of course, you can find repository with my sample project at the end of this post, but for now let’s keep it step by step.

The sample project

The idea behind was to create a scaffolding project that will cope with following popular cases:

  • Downloading data from API while keeping background code simple and readable
  • Showing different object types in one container (list)
  • Saving data to local database

I use public SpaceX API to download three types of data:

  • Their rockets’ fleet
  • Upcoming SpaceX flights
  • And historical events

When the download is finished, data is being saved in local NoSQL database afterwards. To refresh current data, we can use swipe-to-refresh gesture. Moreover, every item is clickable and will navigate to more detailed website, if available. Oh, and there’s a Dark Mode switch if you care about your eyes ;)

Enough of introduction, let’s move to the code.

Networking - Retrofit

There shouldn’t be any surprises - Retrofit provides good abstraction layer over HTTP client and ease conversion between REST API and POJOs. Here’s an example of how to define endpoints and create Retrofit service.

You may notice two new things in the above code:

  1. Each endpoint returns Deferred object instead of Call. More on this in the next section.
  2. We add something called CoroutineCallAdapterFactory() as our call adapter factory in Retrofit builder.

These two will be used in the next section, which are…

(Background) threading - Kotlin coroutines

You may have used some Android classes related to threading in a past - AsyncTasks, Loaders, Executors, or RxJava library. And I have to admit that threading is a really complicated topic of software engineering in general. If you’re curious about its history on Android platform, I recommend to read these great series of articles by Vladimir Ivanov.

But for now, we are in 2019, we can use a great tool called coroutines offered by Kotlin language. You can find more information, for example, in their documentation’s guide. Personally, I think they are great enhancement as we can write asynchronous code like it would be synchronous, which improves readability vastly.

Firstly, let’s add some dependencies in build.gradle file:

We added Kotlin Coroutines dependencies along with Coroutines Adapter made by Jake Wharton. Now it’s possible to use Deferred type in our SpaceX service. You may ask - what’s the Deferred object? If you’re coming from another language to Kotlin, you’ve probably heard of promises or futures. They all have the same purpose - to encapsulate an initialized operation that will be finished sometime in the future. What’s useful for us is that Deferred type is also returned by an async coroutine.

Next, we define our base repository class that will be extended by each data type’s repositories. Here’s a base function to fetch data from the service:

Thanks to reified keyword we can access our generic type T in runtime and with crossinline keyword we assure that we don’t allow any non-local returns in our lambda.

Next, we launch a new coroutine using IO dispatcher which is dedicated for non-blocking tasks, such as database or network requests. We create our request and then jump into main thread using withContext method with Dispatchers.Main parameter. Finally, when we receive a response, we save its body into MutableLiveData observable instance.

Last, but not least, I use a Timber library to efficiently log some possible failures and exceptions in try-catch blocks.

And now you can implement each repository for separate data type, invoke its methods in ViewModels and observe results in Activity. As clean as it could be.

Architecture patterns - MVVM and LiveData

Since announcement of Android Architecture Components in 2017, Google encourages us to build applications based on MVVM architectural pattern. Let’s take a look at it.

Source: https://developer.android.com/topic/libraries/architecture/images/final-architecture.png


This image comes from the Guide to app architecture. It is a recommended way by Google to create Android app architecture and provides clean, modular solution that fulfil Separation of Concerns. We can highlight the following elements:

  • Data source, such as SQLite or web service (in this application ObjectBox database is used instead of Google’s Room)
  • Model - represents app’s business logic (ObjectBox’s entities)
  • Repository - communication bridge between models and presentation layer
  • ViewModel - exposes observable (LiveData) objects and extends default ViewModel’s class
  • Activity/Fragment (View) - represents app UI and observes ViewModel

You may have noticed before some mentions about LiveData. It’s a class from Android Architecture Components that follows the observer pattern. LiveData objects are lifecycle-aware observables, meaning that there will be no memory leaks when navigating between stages of our Android app. It also ensures that shown data in UI is always up-to-date. I really recommend using this class while implementing your own MVVM as it allows us to concentrate on these parts of the app that makes it shine!

This is a sample implementation of ViewModel class:

Pretty straightforward, right? We declare our list of Rocket as a LiveData object and we get them from our Rocket repository. But how do we get this repository?

Inject it! - Koin

If you could ask 10 Android developers about Dagger 2 (the most popular Dependency Injection library), you would probably get 10 different opinions - “it’s great”, “it’s too hard to use/learn”, “I love it”, “I hate it”, “it’s a boilerplate library” etc. However, the truth is Dagger 2 has some terrific functionalities that make it a very useful and popular tool. You should definitely check it out if you haven’t before.

But this blog post isn’t about Dagger 2. In my project I used a lot smaller Kotlin library called Koin. And yes, it’s not a full Dependency Injection library (it rather implements Service Locator pattern), but I think it’s a superior tool when creating small, minimum-boilerplate projects in Kotlin. After all, even Google thinks it’s easier to implement a service locator pattern than to use DI, so who am I to disagree.

Let’s add some dependencies to Gradle…

…and take a look at example modules:

And that’s mostly it! We have a registry that provides dependencies for classes so they don’t have to construct them. You just need to add proper parameters to each class’s constructor and remember to start Koin in your app:

Lastly, I find requesting ViewModels extremely comfortable with Koin’s ViewModel dependency provided in Gradle. This is how your ViewModel will look like:

private val rocketVm: RocketViewModel by viewModel()

Super clean!

Show time - Generic Adapter

How many adapters do you have in your apps? Well, now you need only one! It’s time to implement generic adapter for your all RecyclerViews:

We override three standard Adapter methods and create generic Binder interface that each ViewHolder will implement. That way we can use one adapter for many data types (just a note - I’m implementing here many different lists of same object type, not a list of multiple object types).

Our example ViewHolder might look like this:

I use Kotlin Extensions View Binding instead of Google’s Data Binding, but you can use any of these.

Your tiny storage - ObjectBox

And the last point, we might need to keep downloaded data somewhere locally on the phone. For that, I could use the Room database (which is great by the way) but I thought it would be more instructive to finish my project with ObjectBox implementation. If you haven’t heard of ObjectBox yet, it’s a NoSQL database made by EventBus creators. It is used for mobile devices along with IoT and you should check it out.

After you add ObjectBox into your Gradle file, the next step is to initialize a database:

As documentation says, BoxStore is a “direct interface to the database and manages Boxes”. We create it as a singleton because we should only have one instance running at the same time. Then, we can define its module…

…and inject it where it’s needed (e.g. in our Repository classes). Here’s sample usage how to insert generic list of data into specific Box on background thread:

Even less code than with Room!

Bonus - Dark Mode

This is a bonus addition that I want to show in this blog post. Nowadays, many apps and operating systems are implementing something called “Dark Mode”. It allows users to switch between standard (“Light”) mode and its dark equivalent. Starting from API 28 (Android Pie), we have a possibility to enable Dark Mode:

  • Make sure you have Developer options enabled
  • Find “Night mode” option
  • You can change the behaviour depending on your needs: “automatic”, “always on” and “always off” options

And how to implement it in code? First, I create a helper class:

Next, I inject it into my Activity and set onOptionsItemSelected method properly:

Finally, I set the mode depending on each Android version (for Android Pie and newer, or for Oreo and older):

For greater details, please take a look at this post on Android Developers’ Medium page.

And this is a final user experience of using my sample project:

Conclusion

During my research I’ve seen dozens of deprecated information on how to deal with common Android development problems. Thankfully, we as an Android community are not afraid to share our recent knowledge on blog posts like this. Furthermore, I also learnt many new things while writing this article and I hope my post will help a lot of you to find and follow best current Android practices in 2019.

Thank you for reading, hope you enjoyed it!

Link to repository

Read also

Most Read

1 Mobile payments security. What should developers know about it?
2 Software development view of healthcare wearables
3 7 reasons to use real time data streaming and Flink for your IoT project
4 Creating a digital product for the healthcare industry?
5 How to create an effective Asset Tracking System?

Digital products from concept to launch

We understand that creating a product is a challenging and risky endeavor and believe that having a partner with experience and know-how is a critical first step.

Learn More

The Digital Product Journey

From idea to launch we guide you through the startup experience

Learn More