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ć.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
Android Architecture Components: Getting Started
25 mins
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 ViewModel
s 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:
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 ViewHolder
s 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 View
s 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.
Go to the Developers page, and click “Start Developing Now”. Next, accept the license to sell your soul to the Beer Gods:
Finally, create an API key to use in your requests by clicking “Register A New App” and giving your app the name BottomsUp:
You should see your app now, with the 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.
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 ViewModel
s 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. :]