Android Architecture Components: Getting Started

Take advantage of the new Android Architecture Components in your Kotlin Android app, including Lifecycle, LiveData, and ViewModel. By Filip Babić.

Leave a rating/review
Save for later
Share

Android and the Android SDK have improved a great deal over the years. We’ve received major API upgrades and additions. The UI has changed completely throughout the versions. However, some aspects of the SDK are still challenging, and can leave us with quite a headache.

An example headache is the Android application lifecycle. More often than not it follows the Uncertainty Principle. When you’re not paying attention, it works just like you want it to. But when you’re relying on it, it tends to work its own way. :[

Another issue, which goes along tightly with the lifecycle, is app architecture. Even though Android architecture changes faces about as often as Arya Stark does, the same issues usually remain. Namely, data flow problems, persistence, and stability. But how do you tackle these without complicating your code base?

Believe it or not, you’re now finally able to build a stable architecture in Android. It only took 9 years! And you don’t have to hack, complicate things, or build it yourself! You can do this by using the awesome Android Architecture Components framework from Google.

In this tutorial, you’ll dive into the vast world of Android Architecture Components, model your use case, and build an app to back it all up. In doing so, you’ll learn:

  • Some of the primary Android Architecture Components and why they exist
  • How to structure your app to conform to Model-View-ViewModel (MVVM)
  • How to observe data in a safe way

Note: This Android Architecture Components overview assumes that you’re familiar with the basics of Android development and application structuring. If you’re new to Android, please check out our Beginner Android series and other Android tutorials.

Getting Started

In a little bit, you’ll download the starter project and dive into code. I know you’re hungry for some Kotlin, but you’ll understand the code and logic much better if you learn about the Android Architecture Components in theory first. :]

We’ll be covering Lifecycle, LiveData, and ViewModel, but leaving other Android Architecture Components such as Room for other tutorials.

Lifecycle: Cycling

Android Architecture Components encapsulate three major architectural aspects of app development. And they’ve made way for a new style of application structuring. Even more so, they address issues of stability, data persistence and flow in a very serious manner. With this in mind, we will likely see the quality of Android apps grow in the future like wildfire.

But enough talk: you’re due for an example! Let’s start off with one of the most important concepts, the Lifecycle.

You’ve more likely than not seen the lifecycle graph for activities and fragments. It’s a bit of a mess when you first look at it, isn’t it? Most bad things happen when we do something in the incorrect app state. If something like this popped into your head:

java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState

then you know how frustrating it can be when you forget to unsubscribe a request. To make the Android world a little more crash-free, Google’s built Lifecycle.

It branches out to three concepts, Lifecycle LifecycleObserver, and LifecycleEvent. As a result, you can act on state changes from lifecycle you’re in by annotating methods. This means you can shoot out requests, and update the UI, knowing you’re in safe state.

You might say: “It seems pointless, why don’t I use onCreate or onStart to handle state?”. Well, the Android lifecycle is about as stable as a chair with two legs, so the callbacks don’t provide much safety. So for the most part you’re right, but in some cases callbacks aren’t either accessible or enough.

Have a look at the following example:

class MyLocationListener(private val lifecycle: Lifecycle) : LifecycleObserver {

  @OnLifecycleEvent(Lifecycle.Event.ON_START)
  fun start() {
    if (lifecycle.currentState.isAtLeast(Lifecycle.State.STARTED)) {
      // connect if not connected
    }
  }

  @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
  fun stop() {
    // disconnect if connected
  }
}

Often we need user location updates. But if the user leaves our app, we should stop listening to save data and battery. In fact Lifecycle tells us when it’s safe to listen, and when it isn’t. Moreover, we don’t have to create custom listeners for onStart and onStop events.

To sum up, we’ve seen how this basic yet powerful concept can make our life easier. We’ve seen how easy it is to replace horrid listeners with a LifecycleObserver in our existing code. But we still haven’t addressed the issue of supplying data to the UI!

LiveData: It’s Aliiiiiive!

Continuing in the same spirit, look at another common scenario:

override fun onFailure(call: Call<BeersResponse>?, t: Throwable?) {
  view.hideLoading()
  view.showNoData()
  ...
}

This is a situation we often face, receiving an error from the backend. Of course, we need to handle errors, but how do we do this in a clean way?

In the case that we have a View that displays an error, it is crucial to have both show() and hide() methods for the error. Although this might seem OK, we’re adding methods to the View. Furthermore, the business logic layer shouldn’t know what the view can do and what happens on the UI.

Also, this makes you write a ton of boilerplate interfaces and method declarations for each screen. You know how painstaking that can be. But, once again, Architecture Components save the day. This time featuring LiveData!

What is LiveData made of? Other than sweets and magical essence, it’s basically just an Observable value that is also Lifecycle aware. Quite simple in it’s nature, yet very powerful. It is aware of the app state. So if you do go into background, but your response decides to arrive anyway, the data value is set, but not emitted. Once you’re back in the positive state of mind (read onStart) the data is sent, and the UI gets updated.

The main difference to the typical approach of requesting data from an API is that the UI subscribes to the data, and then knows what to do with it when it arrives. The business logic layer no longer imperatively says what to do.

Although it might seem that you’re moving logic from the business layer into the view, that’s not the case. In reality, all the view knows is that there is data to listen to. You’ll achieve it by subscribing this Lifecycle to a LiveData object.

Since our business logic doesn’t tell the UI what to do, we’ve achieved a nice separation of concerns. That will help us unit test our app. You’ll see how this separation is done later on in the sample project.