CODEX

Android Tutorial Part 1: Using Room with RxJava 2, Dagger 2, Kotlin and MVVM

Fahri Can
CodeX
Published in
6 min readJun 13, 2020

--

Photo by fabio on Unsplash

In this tutorial series, I will show how to use Room Persistence Library, Kotlin, Data Binding, ViewModel, LiveData, Retrofit 2, Glide, RxJava 2, Dagger 2 together with the MVVM architecture pattern to fetch data from GIPHY API. It will be a simple app, which fetches data from an endpoint, stores the data in a table of a database and shows a list of cards in the UI. This is a step by step tutorial.

Prerequisites

You should already be familiar with Kotlin, Retrofit 2, RxJava 2 and Android development. A little bit of experience with Room and Dagger 2 would be nice. The GIPHY API requires an API key. In this tutorial, I will fetch data directly from the Trending Endpoint. I am using Android Studio 3.6.3

Why this article?

Recently I wanted to learn how to persist data from an endpoint. My first problem was there were just a few tutorials where Room and RxJava were used. The second problem was almost no tutorial showed it with Kotlin and MVVM architecture. I thought this must change in the year 2020. This will be just a one screen application, but I am using MVVM so everyone can build something on top.

What the app will look like

The app will contain a list of cards. Each card shows an image, type, username and title. The fetched data will be persisted with Room Persistence Library, so the user can always scroll the list of data even offline.

finished app in portrait mode
finished app in landscape mode

Start new project

Go to Android Studio and create a new Empty Project (Language Kotlin, API 21).

Gradle dependencies

Go to your build.gradle(Module: app) file. At the beginning of the file, you have to enable the Kotlin plugin for the annotation processor. Dagger 2 has for Kotlin a different way to process the annotation than Java. Just paste apply plugin: ‘kotlin-android-extensions’ this plugin below:

apply plugin: 'kotlin-kapt'

Inside defaultConfig {} add javaCompileOptions {} for Room database. Later when everything is implemented this will create a local file for instance “1.json” a local file like this will hold the database schema.

Below buildTypes {} add dataBinding {} and set it to true. Otherwise, you can’t use the Android Architecture component Data Binding.

dataBinding { enabled = true }

Below dataBinding {} add compileOptions {} and kotlinOptions{}. Otherwise, you can’t run the app. This is because when using the latest RxJava 2 features like functional programming, older Java versions (before Java 8) can’t handle them.

compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}

kotlinOptions { jvmTarget = "1.8" }

You will see that buildTypes {}, dataBinding {}, compileOptions {}, kotlinOptions {} are all located inside android {}. Now go to dependencies {} and copy/paste the dependencies I use below:

GIPHY API

Let’s take a look at the Trending Endpoint. Here, you see that a GET request to /v1/gifs/trending which gives an object that contains an array called data and this array contains several objects. Normally one object contains much more information so I edit one, here is a sample of an edited object:

Let’s start development.

Open AndroidManifest.xml and add Internet permissions otherwise, you can’t set a GET request.

<uses-permission android:name="android.permission.INTERNET" />

Create the models

Create a new package and name it model. Copy-paste the JSON object from above. Make a Kotlin data class for the JSON object. I am using an Android Studio plugin called: “JSON To Kotlin Class ​(JsonToKotlinClass)​. I named the first class TrendingResult.kt and the rest will be handled by the plugin. Don’t forget to serialize your properties to use camelCase in the project. Here are the four model classes:

four model classes

Create an interface for the API endpoint

Create a new package and name it data. Inside data create another package and name it network. Inside the network package create an interface and name it GiphyApi.kt. There will be a GET request made to the endpoint “/v1/gifs/trending”. You want a Flowable of TrendingResult as a return type. Flowable because it adds the option to handle backpressure. When there are too many database updates to handle, only the most recent update will be emitted through our reactive stream.

Create the layout for a giphy.

Go to the folder reslayout create a new layout file there and name it item_giphy.xml. Here you will display image, type, username and title as a CardView. Before make sure your root ViewGroup is <layout></layout> so you can use Data Binding.The tags <layout></layout> will create the class ItemGiphyBinding for us. Later, you can use ItemGiphyBinding class in the ViewHolder to tell the Adapter which properties to bind.

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">

<data>

</data>

<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">

</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

Inside <layout></layout> create the tags <data></data> to enable using one of the classes in the layout. Therefore, the tag <variable /> has to be created with a name property type with the location of the class. You can define any name you want with this name. You access the properties of your class defined in type in your layout. In my example, the variable name is data.

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">

<data>

<variable
name="data"
type="com.example.giphyapiandroom.model.Data" />

</data>

<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">

</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

The important thing is your variable with property access is inside “@{}”. This is one-way data binding.

Here is the complete layout for item_giphy.xml:

Take a look at the ImageView with the id: “trending_image” there is the following code line used:

app:imageUrl="@{data.images.fixedHeightSmallStill.url}"

The property imageUrl for app does not exist yet. This is a BindingAdapter and it has to be created. Create a new package and name it internal. Inside internal create a new file call it BindingAdapter.kt. Add the following method inside BindingAdapter.kt:

The important thing is that you put imageUrl as the value inside @BindingAdapter(“”). Then the reference to the layout item_giphy.xml will work.

Time to create the ViewHolder and Adapter

Create a new package and name it view. Inside view create a new package and name it adapter. Create a new Kotlin File in the package adapter. Name the file TrendingViewHolderAdapter.kt.

Start with the ViewHolder

Create a class named TrendingViewHolder which has the field type ItemGiphyBinding in the constructor. Then let your ViewHolder extend from RecyclerView.ViewHolder. The parent class RecyclerView.ViewHolder expects an argument of the type View. Your field of type ItemGiphyBinding contains a property root of the type View. Every class which is created through Data Binding contains this root property of the type View.

class TrendingViewHolder(
val itemGiphyBinding: ItemGiphyBinding
) : RecyclerView.ViewHolder(itemGiphyBinding.root)

Move on to the Adapter

In the same file where the TrenidngViewHolder was created, create the TrendingAdapter above or below it. Let the TrendingAdapter extend from the RecyclerView.Adapter<TrendingViewHolder>(). The Adapter will bind each item on the CardView from item_giphy.xml. Give TrendingAdapter a class property that is from type ArrayList<Data>. Then start to implement the methods onCreateViewHolder(), getItemCount(), onBindViewHolder().

getItemCount() -> Just returns the size of the list property. So the Adapter knows how many cards to create.

override fun getItemCount(): Int = data.size

onCreateViewHolder() -> Normally you would start with LayoutInflater.from() but now you have to use instead DataBindingUtil.inflate(). As you can see, DataBindingUtil.inflate() takes as first argument LayoutInflater.from()

onBindViewHolder() -> Whenever a CardView (from item_giphy.xml) for the item gets created it will be fetched from the current position of the data list.

override fun onBindViewHolder(holder: TrendingViewHolder, position: Int) {
holder.itemGiphyBinding.data = data[position]
}

Okay, you are almost finished with the TrendingAdapter. You need one more method, which clears the existing items in the list. It should also add all new items and notifies other components about the changed dataset.

fun setUpData(giphies: List<Data>) {
data.clear()
data.addAll(giphies)
notifyDataSetChanged()
}

Okay, that’s it for the first part, here is the next part: Part 2

Here is the completed project, check out branch part1:

--

--

Fahri Can
CodeX

Android Dev | Blogger | AI & Crypto Enthusiast