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:
- Each endpoint returns
Deferredobject instead of
Call. More on this in the next section.
- 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 -
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:
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
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.
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()
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
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:
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!