Android architecture components are essentially a collection of libraries that help us developers build testable, maintainable and robust native Android apps. This is one of the four components of Android Jetpack. Android Jetpack components bring together the existing Support Library and Architecture Components to bring exciting libraries which greatly reduce boilerplate code and increase consistency of the codebase across multiple Android versions and devices

Some of the Android Architecture Components include:
- Data Binding: It helps to bind your layout XML UI elements to data sources of our app.
- Lifecycles: It manages activity and fragment lifecycles of our app, survives configuration changes, avoids memory leaks and easily loads data into our UI.
- LiveData: This is a lifecycle, aware observable data holder. This lets the components in your app observe LiveData objects for changes
- Navigation: It handles everything needed for in-app navigation in Android application.
- Paging: It helps in loading data partially and in chunks to allow for efficient use of system resources.
- Room: Room provides an abstraction layer over SQLite to allow fluent database access while harnessing the full power of SQLite.
- ViewModel: The ViewModel class is designed to hold and manage UI-related data in a lifecycle conscious way. This allows data to survive configuration changes such as screen rotations.
- WorkManager: It manages every background jobs in Android with the circumstances we choose allowing for increased backward compatibility.
We will focus this article on DataBinding, ViewModel and LiveData.We will use the MVVM(Model-View-ViewModel) architecture pattern with Kotlin as the programming language
CocktailsDB API
CocktailsDB is an open,crowd-sourced database consisting of hundreds of drinks and cocktails from all over the world. For the purpose of this article, we will use their excellent free JSON API. We will proceed to make a simple app that queries this API and gets a list of alcoholic cocktails and populate them in a RecyclerView on the app using as many Architecture components along the process. Consider upgrading to their premium service as well, if you enjoyed using the API, to unlock extra features
Analyzing the Recipe
Let’s have a look at the API endpoint
https://www.thecocktaildb.com/api/json/v1/1/filter.php?a=Alcoholic
The “a” is a Query parameter. This is a key-value pair to communicate how you want to proceed to filter the cocktails in the database. The “a” indicates you wish you filter by whether the cocktails are alcoholic or not. The value is “Alcoholic” as our intended outcome of the app is to filter by alcoholic cocktails only
The response(truncated for display purposes)
{
"drinks": [
{
"strDrink": "'57 Chevy with a White License Plate",
"strDrinkThumb":
"https://www.thecocktaildb.com/images/media/drink/qyyvtu1468878544.jpg",
"idDrink": "14029"
},
{
"strDrink": "Archbishop",
"strDrinkThumb": "https://www.thecocktaildb.com/images/media/drink/4g6xds1582579703.jpg",
"idDrink": "11052"
},
{
"strDrink": "Arctic Fish",
"strDrinkThumb": "https://www.thecocktaildb.com/images/media/drink/ttsvwy1472668781.jpg",
"idDrink": "14622"
},
{
"strDrink": "Arctic Mouthwash",
"strDrinkThumb": "https://www.thecocktaildb.com/images/media/drink/wqstwv1478963735.jpg",
"idDrink": "17118"
},
{
"strDrink": "Arise My Love",
"strDrinkThumb": "https://www.thecocktaildb.com/images/media/drink/wyrrwv1441207432.jpg",
"idDrink": "11053"
}
]
}
The response returns in essence a JSON array “drinks” with each drinks item having 3 properties; The cocktail name, the cocktail thumbnail picture and a unique identifier for the cocktail. This data will consequently be used in populating the RecyclerView item later
Prerequisites
To follow this tutorial, you should have basic knowledge of working with:
- Kotlin
- Retrofit(Networking Library)
- Coroutines
Let’s start mixing
Proceed to Android Studio and create a Kotlin Project

The Ingredients
Add the following dependencies in the root-level build.gradle file
buildscript {
ext.kotlin_version = '1.3.72'
ext.lifecycle_version = '2.2.0'
ext.retrofit_version = '2.7.1'
ext.gson_version = '2.7.1'
//...
}
Then the following dependencies in app level build.gradle file
The Model
The model is responsible for providing data sources to the ViewModel, including entity classes, network requests, and local storage

As we can see, ViewModel knows Model but does not know View and View can know ViewModel but does not know Model. This is the abstraction that MVVM achieves, the separate layers of presentation layer, domain layer(business logic) and data layer(data access and storage). This separation of concerns allows for easier maintainability and testability of our Android apps.
data class Drinksitem (
var idDrink: String,
var strDrink: String,
var strDrinkThumb: String)
data class DrinksList (
var drinks:List
)
Create a package called model. In it, setup these two data classes; DrinkList references the first object in the json response, the drinks JSONArray with the cocktail items. Consequently, DrinksItem is the model class for the individual cocktail item
The Network
We will use Retrofit library and Coroutines for the networking calls. Coroutines are used in combination with Retrofit to make API calls in a separate asynchronous thread.
Let us start with the Retrofit Service class
interface DrinksApi {
@GET("filter.php?")
suspend fun filterbycategory(@Query("a") c: String?):
DrinksModel
}
NB: We will revisit the suspend keyword later in the article
Retrofit Builder Class:
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
object RetrofitBuilder {
private const val BASE_URL =
"https://www.thecocktaildb.com/api/json/v1/1/"
private fun getRetrofit(): Retrofit {
return Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build() //Doesn't require the adapter
}
val apiService: DrinksApi =
getRetrofit().create(DrinksApi::class.java)
}
We are using the repository pattern. When the ViewModel requests the data that will be used for the View, the Repository will provide you with this by choosing the appropriate data locally or on the network. At this point, the ViewModel doesn’t care if the requested data was retrieved locally or from the network. You can make it independent of a particular implementation by using Repository as an interface and creating an implementation. For this tutorial, however, we are fetching the data from the network so the repository will conform to that
class DrinksRepo(private val apiService: DrinksApi) {
suspend fun getDrink(category: String) =
apiService.filterbycategory(category)
}
The UI State
We need a utility class that will be responsible to communicate the current state of Network Call to the Presentation Layer. Create a package utils and add the following classes
enum class StateStatus {
SUCCESS,
ERROR,
LOADING
}
data class Resource(val status: StateStatus, val data: T?, val message: String?) {
companion object {
fun success(data: T): Resource =
Resource(status = StateStatus.SUCCESS, data = data,
message = null)
fun error(data: T?, message: String): Resource =
Resource(status = StateStatus.ERROR, data = data,
message = message)
fun loading(data: T?): Resource =
Resource(status = StateStatus.LOADING, data = data,
message = null)
}
}
The ViewModel
Now we are ready for the ViewModel.The ViewModel architecturally is responsible for the application logic. Our ViewModel will fetch the cocktails from the repository using a coroutine
We also create a BindingAdapter.A binding adapter is simply a static or instance method that is used to manipulate how some user-defined attributes map data-bound variables to views. We use Glide, an excellent image loading library to efficiently load an image into the ImageView of the recyclerview item we will define later.BindingAdapters allow for re-use of logic that apply to view properties throughout the codebase
To instantiate a ViewModel you need a ViewModelFactory: it’s a class that implements ViewModelProvider.Factory and it will create the ViewModel from a parameter .class
The View
Let us set up the MainActivity class below
The respective layout
<?xml version="1.0" encoding="utf-8"?><androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone" />
<ProgressBar android:id="@+id/progressBar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" /></androidx.constraintlayout.widget.ConstraintLayout>
So what is happening here? We start by initialising the ViewModel with the required dependency. We then proceed by setting the appropriate layout manager for the recyclerview. The method set up observers() is responsible for calling the ViewModel method that contains our logic. Dependent on the 3 states of the networking call, each state is handled appropriately. On successful response, the JSONArray is parsed to extract the list of cocktails which is subsequently passed to the recycler view adapter.
The Adapter
Let’s have a look at the Adapter
The respective layout
Data Binding
Data Binding uses declarative layouts and minimises the glue code between programming code and XML. Let us enable this by editing the app level build.gradle file
android {
dataBinding {
enabled = true
}
// ...
}
The Row Item ViewModel
package com.cocktails.mydrinksapp.viewmodels
import android.R
import android.content.Context
import android.content.res.ColorStateList
import android.util.Log
import android.widget.ImageView
import androidx.databinding.BindingAdapter
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import com.bumptech.glide.Glide
import com.bumptech.glide.request.RequestOptions
import com.cocktails.mydrinksapp.model.DrinksItem
class RowItemViewModel: ViewModel() {
private val thumbnail = MutableLiveData()
private val title = MutableLiveData()
fun bind(child: DrinksItem){
thumbnail.value = child.strDrinkThumb
title.value = child.strDrink
}
fun getThumbnail():MutableLiveData{
return thumbnail
}
fun getTitle():MutableLiveData{
return title
}
}
app:imageUrl=”@{viewModel.thumbnail}” and android:text=”@{viewModel.title}” attributes are used to display the data in the imageview and Textview respectively from the LiveData elements in RowItemViewModel.
The bound data(thumbnail and title) are LiveData objects. Remember LiveData is lifecycle-aware, meaning it respects the lifecycle state of the app components and ensures that LiveData only updates the component (the observer) when it’s in an active lifecycle state. This behaviour prevents object leaking and ensures the app doesn’t do more work than it should.
Run your app now and you should see something like this

The Garnish
This tutorial was meant to be a very basic and brief introduction to Android Architecture Components showcasing how to consume the CocktailsDB API. Here are tips on how to expand this app using Architecture Components
- Dependency Injection using Hilt
- Storing the data from remote sources locally for offline usage using Room
- Efficiently load and display huge chunks of data using Paging
Find the full source code in this Github link. Find as well a production app I built using this API here.
“Good source code is magic,but with words”