Africa\'s Talking
Android Brewing Android Architecture Components using CocktailsDB API

Brewing Android Architecture Components using CocktailsDB API

-

- Advertisment -

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

  1. Dependency Injection using Hilt
  2. Storing the data from remote sources locally for offline usage using Room
  3. 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”

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Latest Posts

Building Application using Node.js HTTPS Module and Africa’s Talking SMS API

Introduction For a long time, HTTP (Hypertext Transfer Protocol) was responsible for communication between a client application and a server...

Building an Exam Enrollment and Grade Notification System using Africa’s Talking SMS Shortcode API in Django

Introduction This article explores the application of SMS shortcodes to create transparency and improve the quality of education in learning...

Build a user account management system using USSD and SMS API in Go

Introduction We will be learning how to use both the Africastalking SMS and USSD api by building an application where...

Date & Time Analysis With R

One will learn how to analyse dates and time; reproducing date-related columns from a date and also format dates and time. In application, one will learn how to plot simple and multiple time series data using the R language.
- Advertisement -

Two Factor Authentication With PHP and Africa’s Talking SMS API

Two factor authentication is an additional layer of security used to ensure only authenticated users gain access to an online account. because Passwords are historically weak, and can be easily stolen, it can't alone be the way that users access their accounts.

Building a masked number system using Spring Boot, Android and Voice Apis from Africa’s Talking

Introduction In this walk through we shall be building a platform to enable a hypothetical company have their agents call...
- Advertisement -

You might also likeRELATED
Recommended to you