Android Jetpack Architecture Components: Getting Started

In this tutorial, you will learn how to create a contacts app using Architecture Components from Android Jetpack like Room, LiveData and ViewModel. By Zahidur Rahman Faisal.

Leave a rating/review
Download materials
Save for later
Share

Developing Android apps is just like playing video games — it’s challenging and rewarding at the same time! While enjoying your journey through the Android world, you must have faced many obstacles and wished for some kind of Jetpack to jump through those obstacles, or that you had a nitro boost to kickstart your development engine for the sprint…

Google heard you – now Android literally offers you a Jetpack!

Android Jetpack is a set of libraries that helps you to deal with the challenges you eventually face as a Android Developer — writing boilerplate code, managing activity lifecycles, surviving configuration changes or preventing memory leaks.

In this tutorial, you’ll create a contact list app called iMet using Android Jetpack, which stores contact information about the people you have met.

In the process, you’ll learn:

  • To create a Room database to store and retrieve data.
  • To use ViewModel to isolate use cases from the View.
  • To view data and observe changes using LiveData.
  • To simplify navigation and data passing within the app using Navigation Components.
  • And More!

Getting Started

Get started by downloading and installing the preview release of Android Studio from here.

Download Android Studio Canary

Note: You need to have Android Studio Canary Build 3.3 to use Navigation Components in your project. The Navigation Components are an experimental feature in Android Studio 3.2. You can use Android Studio Stable for your ongoing projects and Android Studio Preview to explore new features at the same time. If you already have Android Studio Canary Build 3.3, then you can continue reading!

Download the zip file containing the starter project for this tutorial using the Download materials button at the top or bottom of this tutorial.

Launch Android Studio 3.3 Preview

Now, on macOS launch Android Studio 3.3 Preview from your Applications folder as shown above and select Open an existing Android Studio project to import the starter project that you just downloaded. On Linux and Windows, follow the steps for your OS to start Android Studio 3.3 Preview and open the starter project.

Note: You may be asked to update your gradle plugin after opening the app. Go ahead and update!

Build and run using keyboard shortcut Shift + F10 (or Control + R if you’re on macOS). If you see a list of awesome people like following, you’re all set to dive in!

Starter project

Start exploring the PeopleRepository class inside the data package in the starter project. PeopleRepository is the gatekeeper of the data layer in your app. It holds all data regarding People and provides information to different segments in the app — for example: PeoplesListFragment.

You must be thinking, “What’s the source of data for PeopleRepository?!”

What is the source?

Well, assume PeopleInfoProvider, inside the net package within data, is your helper class that fetches People information from some web-service and stores it in PeopleRepository.

But how about adding the People that you’ve just met today into the PeopleRepository so that you can contact them later?

To achieve that, you need to update PeopleRepository to store data from all your sources (local or remote), making it a single source of truth for managing the People information.

Note: To learn more about Repository Pattern, you may follow the official App Architecture guide.

So, here’s your first challenge – adding persistence!

Adding Dependencies for Architecture Components

Jetpack depends on a few external libraries, so you need to add those dependencies first. Open the build.gradle file from your app module and append the following lines just above the closing brackets of the dependencies section:

// 1: Room Components
def roomVersion = "1.1.1"
implementation "android.arch.persistence.room:runtime:$roomVersion"
kapt "android.arch.persistence.room:compiler:$roomVersion"

// 2: Lifecycle Components
def lifecycleVersion = "1.1.1"
implementation "android.arch.lifecycle:extensions:$lifecycleVersion"

// 3: Navigation Components
def navigationVersion = "1.0.0-alpha04"
implementation "android.arch.navigation:navigation-fragment-ktx:$navigationVersion"
implementation "android.arch.navigation:navigation-ui-ktx:$navigationVersion"

These dependencies provide components with the following capabilities:

  1. Room Components: Add local persistence in your app. Room is an object-mapping library wrapping SQLite to make your database management concise and painless.
  2. Lifecycle Components: Lift the responsibility of managing your app’s lifecycle with ease. They add lifecycle-aware components like ViewModel and LiveData that allows you to forget about writing code for handling configuration changes or loading data into your UI when there’s an update.
  3. Navigation Components: Add helper classes like NavController to simplify navigation and passing of data throughout your app.

Creating ROOM for Your Contacts

To pass your first challenge, persistence, you need to implement three major components from Room:

  1. Entity: A model class that represents a table in a Room database.
  2. Data Access Object (DAO): A helper class to access and query the database.
  3. Database: An abstract class that directly extends RoomDatabase. It’s main responsibility is creating the database and exposing entities through Data Access Objects (DAO).

Creating Entities

Start with the simplest component first. You already have a People class inside the model which is nested in the data package; now, modify it to match the following to declare it as an Entity for your Room database:

package com.raywenderlich.android.imet.data.model

import android.arch.persistence.room.Entity
import android.arch.persistence.room.PrimaryKey

@Entity
data class People(
    var name: String = "",
    var metAt: String = "",
    var contact: String = "",
    var email: String = "",
    var facebook: String = "",
    var twitter: String = "",
    @PrimaryKey(autoGenerate = true) var id: Int = 0
)

Here, you’re using two annotations from Room:

  • @Entity: Declares that you’re going to use this model as an Entity.
  • @PrimaryKey: Defines id as the Primary Key for the Entity. Adding autoGenerate = true ensures id will be automatically generated whenever you create a new record in the database with this Entity.

Piece of cake! Moving on to the next one…

Creating a Data Access Object (DAO)

A DAO is basically an interface to access required data from your database. It has two sole purposes:

  1. It saves you from writing direct queries, which are more error-prone and harder to debug.
  2. It isolates query logic from database creation and migration code for better manageability.

Create a new package for the DAO and Database classes. Right-click on the data package, then select New ▸ Package.

New package

Name it db inside the New Package dialog and click OK.

New Package dialog

Create a new Kotlin Interface inside the db package and name it PeopleDao.

New Interface

As you’re going to use this class for database queries, implement the most common queries — Select All, Insert, Delete and Select by ID. Replace everything inside the PeopleDao file with following:

package com.raywenderlich.android.imet.data.db

import android.arch.persistence.room.Dao
import android.arch.persistence.room.Insert
import android.arch.persistence.room.OnConflictStrategy
import android.arch.persistence.room.Query
import com.raywenderlich.android.imet.data.model.People

@Dao
interface PeopleDao {

  // 1: Select All
  @Query("SELECT * FROM People ORDER BY id DESC")
  fun getAll(): List<People>

  // 2: Insert
  @Insert(onConflict = OnConflictStrategy.REPLACE)
  fun insert(people: People)

  // 3: Delete
  @Query("DELETE FROM People")
  fun deleteAll()

  // 4: Select by id
  @Query("SELECT * FROM People WHERE id = :id")
  fun find(id: Int): People

}

That’s pretty straight forward! Similar to the @Entity annotation, here, you use @Dao on top of the interface to declare it as a DAO for your Room database.

Querying is fairly simple in Room. Reviewing the methods in PeopleDao one by one:

  1. getAll() returns a list of People entities. The @Query annotation on top performs a selection on your database and returns all People as a list. In this query, SELECT * FROM People, actually does the job and ORDER BY id DESC prepares the list with a descending order of people’s id.
  2. insert(people: People) inserts new People entities in your database. While using the @Insert annotation is enough to perform the insertion for you, you can additionally add onConflict = OnConflictStrategy.REPLACE for cases wherein your new People entity has the same id of an existing one in your database. In that case, it’ll replace (or update) the existing entity.
  3. deleteAll() does exactly as its name implies — you perform a DELETE operation on the People entity in your database (remember, an Entity is like a Table in Room).
  4. find(id: Int) is to find a People entity with specific id. It executes a SELECT query with a condition where the People‘s id is matched to the supplied id parameter in this function.