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
You are currently viewing page 2 of 3 of this article. Click here to view the first page.

ViewModel: Modelling Gig

Luckily I’m not talking about fashion models, it’s not really my strong suit. The type of models we’ll be confronting are ViewModels!

In order to understand them we’ll go through an example in the sample project. For now, you need to know ViewModels are persistent throughout a Lifecycle scope. Meaning, if you create it in an Activity, as long as the Activity lives in the stack, so does the ViewModel. Knowing this, we can create a ViewModel in Activity scope, and use it in multiple Fragments, effectively sharing data between them.

Activities and fragments are owners of a Lifecycle, as they implement the LifecycleOwner interface. This means they can provide their instance of Lifecycle when asked.

Also, calling for ViewModel instances will always return the Lifecycle bound instance. They even persist through orientation changes! So, by combining LiveData with ViewModels, you have data persistence such as for the savedInstanceState. This will save you from tons of crashes!

You’ve survived my excruciatingly long introduction, way to go! :]

One Project to Implement Them All

In order to show you the full extent of Android Architecture Components, we’ve created a sample project. Also, we’ve found a real life example to make each component as clear as possible. Shall we begin?

Start off by downloading the sample starter project here. Open the project in Android Studio 3.0.1 or greater and build and run to make sure all is well:

Starter app

Let’s go over the project structure, which you can see is modular by package. The api and interaction packages are part of the model layer, the connection to backend. The di package contains, well – the dependency injection setup! The rest should be pretty self explanatory, with ui containing Android specific files and models containing our data classes. We’ve also added all of the needed dependencies to Gradle and separated them a bit for better understanding.

You’re using Retrofit for network requests, Glide for image loading, and Dagger for dependency injection.

The model classes look as follows:

data class Beer(val name: String = "",
                val style: BeerStyle = BeerStyle(),
                val labels: BeerLabels = BeerLabels())

data class BeerLabels(val large: String = "",
                      val medium: String = "")

data class BeerStyle(val name: String = "")

data class BeerResponse(@SerializedName("data") val beers: List<Beer>,
                        val currentPage: Int)

The labels contain your beer images so you can see what you drink. You’ll also display the beer name, and a style. The beer response models the beer data coming back from the API.

The ViewHolders to display the beers are also prepared for you in the starter project, and they’re fairly simple:

class BeerHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {

  fun showBeer(beer: Beer): Unit = with(itemView) {
    beerStyle.text = beer.style.name
    beerName.text = beer.name

    val mediumImage = beer.labels.medium
    val largeImage = beer.labels.large

    // load whichever image exists
    Glide.with(itemView).load(if (largeImage.isNotBlank()) {
      largeImage
    } else {
      mediumImage
    }).into(beerImage)
  }
}

Since we’re using Kotlin Android Extensions, we don’t have to manually initialize Views using findViewById.

Waiter, I’ll have a beer please :]

We’ve chosen the best sample app purpose we could: beer drinking! However, beers don’t magically appear in your hands (sadly). You must first create an application at the BreweryDB. Create a new account, if you haven’t got one already.

Create BreweryDB account

Go to the Developers page, and click “Start Developing Now”. Next, accept the license to sell your soul to the Beer Gods:

BreweryDB TOS

Finally, create an API key to use in your requests by clicking “Register A New App” and giving your app the name BottomsUp:

BreweryDB Register New App

You should see your app now, with the API key:

BreweryDB API Key

It may take a day or two for BreweryDB to approve your app, so you’ll see a “Pending Activation” message on the app screen. Calls to the API won’t work until the app is approved.

Since our app is simple, you can store your new key at the end of AppConstants.kt:

const val API_KEY = "your-api-key"

If this were a production app, you’re advised you to keep the key in a safe environment.

Mmmmm Beer

Hitchhiking Through Components

While 42 may be the answer to what life is about, this app will be about a reactive, lifecycle aware structure. You’ve got a backend now, and most of the core setup, but you need to integrate the Architecture Components. With LiveData you will make the UI update on data changes. ViewModel will make the requests survive orientation changes. Additionally, you’ll safely execute requests in the proper app state thanks to Lifecycle.

You’ll start off by requesting beers from the backend API. Then you’ll supply the ViewModel‘s LiveData mini cooler with said beers. And last but not least, you’ll provide the view with a cold one from the ViewModel by letting it subscribe to LiveData changes.

Building your ViewModel

Start by creating a viewmodel package in the app root. Then create a BeersViewModel class, which extends ViewModel, in the package:

class BeersViewModel : ViewModel() {
}

Your ViewModel needs a reference to the BreweryInteractor prepared in the starter project. Do this by adding a lazily computed value named interactor, at the top of the class, provided by the AppComponent from the application subclass App:

private val interactor by lazy { App.component.breweryInteractor() }

Next, you need to request beers from the interactor. For now, add a method getBeers() to request the first page of beers, and add a callback in your ViewModel just under the variable declarations:

fun getBeers() {
  interactor.getBeers(1, beersCallback())
}

private fun beersCallback() = object : Callback<BeerResponse> {
  override fun onFailure(call: Call<BeerResponse>?, t: Throwable?) {
  }

  override fun onResponse(call: Call<BeerResponse>?, response: Response<BeerResponse>?) {
  }
}

When prompted to import classes such as Code or Response, be sure to use the Retrofit2 classes.

Now you have everything a ViewModel needs, except for LiveData (which you’ll add below)! :]

One last thing you need to do before you move on to LiveData is add the ViewModel to the top of BeersActivity.kt:

private val viewModel by lazy { getViewModel<BeersViewModel>() }

The sample project comes with a few neat extension functions that help to tidy up your code. So instead of calling ViewModelProviders.of(target).get(viewModelClass), here you instead call getViewModel().

You might have noticed how your DI is currently done by lazy values. This is not necessarily the best approach, but Dagger and ViewModels don’t work together out of the box. In order to create a ViewModel with Dagger, you have to declare a custom Factory, which is done by using mapped binding or subcomponents.

For the purposes of simplicity though, the project will use lazy values. You can check out Google’s sample app on Architecture Components to see the subcomponent approach. :]