All days
Day 09Module 5 · Persistence· 5–7 hrs

Room I — Entities, DAO, ViewModel, RecyclerView (ToDo app)

Build a persistent ToDo app. Room wraps SQLite. Repository + ViewModel keep DB access off the UI. RecyclerView lists items efficiently.

0% done

Key concepts

  • @Entity, @PrimaryKey
  • @Dao (Insert, Query, Update, Delete)
  • @Database (RoomDatabase)
  • Repository pattern
  • RecyclerView + ListAdapter + DiffUtil

Lectures in this day

  1. 01

    Project Setup

    Before writing features, get the project ready. Turn on Data Binding, Navigation, Safe Args, and add Room (the database library) in Gradle. `minSdk` is the oldest Android version your app supports.

    Real life · Prepping your kitchen before cooking — knives sharpened, oven on, ingredients ready. Ten minutes now saves an hour later.
    gradle
    plugins {
        id("com.android.application")
        kotlin("android")
        kotlin("kapt")                                    // needed for Room
        id("androidx.navigation.safeargs.kotlin")
    }
    dependencies {
        implementation("androidx.room:room-runtime:2.6.1")
        implementation("androidx.room:room-ktx:2.6.1")
        kapt("androidx.room:room-compiler:2.6.1")
    }

    Practice · 3 quick questions

    1. Q1.Project Setup means…

    2. Q2.Dependencies are added in…

    3. Q3.A good first check is…

    Pick one answer per question.
  2. 02

    New ToDo Fragment Page Design

    Draw the 'add todo' screen: a title box, a description box, a button to pick date/time, and a Save button. Simple form, one action.

    Real life · Like a sticky note on the fridge — but with a date field so 'buy cake' becomes 'buy cake, Saturday 5 pm'.
    xml
    <class="tok-key">LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:orientation="vertical" android:padding="16dp"
        android:layout_width="match_parent" android:layout_height="match_parent">
    
        <class="tok-key">EditText android:id="@+id/title" android:hint="Title"
                  android:layout_width="match_parent" android:layout_height="wrap_content" />
    
        <class="tok-key">EditText android:id="@+id/desc"  android:hint="Description"
                  android:layout_width="match_parent" android:layout_height="wrap_content" />
    
        <class="tok-key">Button   android:id="@+id/pickDate" android:text="Pick date & time"
                  android:layout_width="match_parent" android:layout_height="wrap_content" />
    
        <class="tok-key">Button   android:id="@+id/save" android:text="Save"
                  android:layout_width="match_parent" android:layout_height="wrap_content" />
    </class="tok-key">LinearLayout>

    Practice · 3 quick questions

    1. Q1.New ToDo screen mainly needs…

    2. Q2.Text input widget?

    3. Q3.Best container for a form?

    Pick one answer per question.
  3. 03

    Retrieve Values from Room Group

    Read what the user typed into an EditText with `.text.toString()`. Trim spaces if needed. These raw strings become the fields you save into the database.

    Real life · Reading answers from a paper form before filing it away — each field goes into your record.

    Practice · 3 quick questions

    1. Q1.Getting values from EditText uses…

    2. Q2.Empty input should be…

    3. Q3.Trim whitespace with…

    Pick one answer per question.
  4. 04

    Date/Time Picker Dialog

    Show a DatePickerDialog to pick the day, then a TimePickerDialog for the hour. Combine the two into a `Calendar`, then save `timeInMillis` (a single number of milliseconds since 1970). Numbers are easy to compare and sort.

    Real life · Setting an alarm: pick the day, then the time. Behind the scenes the phone stores it as one number.
    kotlin
    val cal = Calendar.getInstance()
    
    DatePickerDialog(requireContext(), { _, y, m, d ->
        cal.set(y, m, d)                                  0
    
        TimePickerDialog(requireContext(), { _, h, min ->
            cal.set(Calendar.HOUR_OF_DAY, h)              1
            cal.set(Calendar.MINUTE, min)
            dueAt = cal.timeInMillis                      2
        }, cal.get(Calendar.HOUR_OF_DAY), cal.get(Calendar.MINUTE), true).show()
    
    }, cal.get(Calendar.YEAR), cal.get(Calendar.MONTH), cal.get(Calendar.DAY_OF_MONTH)).show()

    Practice · 3 quick questions

    1. Q1.Date picker widget?

    2. Q2.Time picker widget?

    3. Q3.They return values via…

    Pick one answer per question.
  5. 05

    Create ToDo Model + Setup Navigation

    First say what a ToDo IS with a `data class` — its fields (id, title, description, dueAt, done). Then draw the flow: List screen → Add screen → back to List (which refreshes).

    Real life · Deciding what a 'note' contains in your notebook, then labeling the tabs: All Notes and New Note.
    kotlin
    data class Todo(
        val id: Long = 0,           0
        val title: String,
        val description: String,
        val dueAt: Long,            1
        val done: Boolean = false,
    )

    Practice · 3 quick questions

    1. Q1.A Model class holds…

    2. Q2.Navigation between form and list uses…

    3. Q3.A Kotlin data class gives you…

    Pick one answer per question.
  6. 06

    Room Library Overview

    Room is Google's easy way to use SQLite (a small database inside the phone). It has 3 parts: `@Entity` (a table = a data class), `@Dao` (the queries you can run), and `@Database` (the container that ties everything together).

    Real life · A filing cabinet: entities are the drawer templates, DAOs are the clerk who files and fetches, the database is the cabinet itself.

    Practice · 3 quick questions

    1. Q1.Room is a wrapper around…

    2. Q2.Three main Room parts?

    3. Q3.Room checks SQL at…

    Pick one answer per question.
  7. 07

    Define ToDo Entity

    Turn your `Todo` data class into a table by adding `@Entity` and marking one field as the primary key (a unique id per row). `autoGenerate = true` lets Room pick the id for you.

    Real life · Deciding the shape of the form that will go into the filing cabinet — every filed form uses the same layout.
    kotlin
    @Entity(tableName = "todos")
    data class Todo(
        @PrimaryKey(autoGenerate = true) val id: Long = 0,
        val title: String,
        val description: String,
        val dueAt: Long,
        val done: Boolean = false,
    )

    Practice · 3 quick questions

    1. Q1.@Entity marks…

    2. Q2.Primary key uses…

    3. Q3.Field → column uses…

    Pick one answer per question.
  8. 08

    Define Room Database

    Write an abstract class that extends `RoomDatabase` and lists your entities. It exposes DAOs (the query classes). You build it once with `Room.databaseBuilder(...)` and reuse the same instance everywhere (a singleton).

    Real life · The cabinet itself, bolted to the floor. You buy one; every clerk borrows keys from it.
    kotlin
    @Database(entities = [Todo::class], version = 1)
    abstract class AppDb : RoomDatabase() {
        abstract fun todoDao(): TodoDao        0
    }

    Practice · 3 quick questions

    1. Q1.The database class extends…

    2. Q2.It exposes…

    3. Q3.You build it with…

    Pick one answer per question.
  9. 09

    Insert, Query, Database Inspector

    In the DAO you write methods with annotations: `@Insert`, `@Update`, `@Delete`, and `@Query("SELECT ...")` for reading. Android Studio's Database Inspector lets you peek at the rows while your app is running.

    Real life · Adding a form to the cabinet, then peeking through a glass door to make sure it really got filed.
    kotlin
    @Dao
    interface TodoDao {
        @Insert suspend fun insert(t: Todo): Long
        @Update suspend fun update(t: Todo)
        @Delete suspend fun delete(t: Todo)
    
        @Query("SELECT * FROM todos ORDER BY dueAt")
        fun observeAll(): LiveData<List<Todo>>     0
    }

    Practice · 3 quick questions

    1. Q1.@Insert methods should be marked…

    2. Q2.@Query annotation takes…

    3. Q3.Database Inspector is used to…

    Pick one answer per question.
  10. 10

    Implement ViewModel and Repository Pattern

    A Repository is a middle layer — the ONLY place that talks to the database. The ViewModel calls the Repository, never the DAO directly. This makes it easy to change where data comes from later (e.g. add the internet).

    Real life · A hotel concierge: guests always ask the concierge, who decides whether to call the kitchen, the laundry, or the driver.
    kotlin
    class TodoRepository(private val dao: TodoDao) {
        val all: LiveData<List<Todo>> = dao.observeAll()
        suspend fun add(t: Todo) = dao.insert(t)
        suspend fun toggle(t: Todo) = dao.update(t.copy(done = !t.done))
        suspend fun delete(t: Todo) = dao.delete(t)
    }
    
    class TodoViewModel(private val repo: TodoRepository) : ViewModel() {
        val todos = repo.all
        fun add(title: String, desc: String, dueAt: Long) =
            viewModelScope.launch {                        0
                repo.add(Todo(title = title, description = desc, dueAt = dueAt))
            }
    }

    Practice · 3 quick questions

    1. Q1.Repository pattern hides…

    2. Q2.ViewModel exposes data as…

    3. Q3.The benefit is…

    Pick one answer per question.
  11. 11

    RecyclerView and Adapter Concept

    A RecyclerView shows a long list without wasting memory. It builds only enough item views to fill the screen, then reuses them as you scroll. You provide an Adapter — the small helper that binds one row of data to one row's view.

    Real life · A sushi conveyor belt with 10 plates. Empty plates roll back to the kitchen and get refilled — no need to make a plate for every guest.

    Practice · 3 quick questions

    1. Q1.RecyclerView is for…

    2. Q2.You need an…

    3. Q3.Compared to ListView, RecyclerView…

    Pick one answer per question.
  12. 12

    Implement RecyclerView with ToDo Items

    Use `ListAdapter` with a `DiffUtil` callback so only the changed rows animate. Inside `onBindViewHolder`, set the title, due date, and a checkbox for done — plus a click listener to toggle it.

    Real life · A conveyor belt that knows which plates changed and only refreshes those. Everyone else keeps eating.
    kotlin
    class TodoAdapter(val onToggle: (Todo) -> Unit) :
        ListAdapter<Todo, TodoAdapter.VH>(DIFF) {
    
        class VH(val b: ItemTodoBinding) : RecyclerView.ViewHolder(b.root)
    
        override fun onCreateViewHolder(p: ViewGroup, v: Int) =
            VH(ItemTodoBinding.inflate(LayoutInflater.from(p.context), p, false))
    
        override fun onBindViewHolder(h: VH, pos: Int) {
            val t = getItem(pos)
            h.b.title.text = t.title
            h.b.done.isChecked = t.done
            h.b.done.setOnClickListener { onToggle(t) }
        }
    
        companion object {
            0
            val DIFF = object : DiffUtil.ItemCallback<Todo>() {
                override fun areItemsTheSame(a: Todo, b: Todo) = a.id == b.id
                override fun areContentsTheSame(a: Todo, b: Todo) = a == b
            }
        }
    }

    Practice · 3 quick questions

    1. Q1.Row layout for each item lives in…

    2. Q2.onBindViewHolder should…

    3. Q3.onCreateViewHolder should…

    Pick one answer per question.

Real-life analogies

Room

A physical filing cabinet with typed drawers. Entities = drawer templates. DAO = the clerk who files & fetches things for you.

RecyclerView

A conveyor belt of ~10 plates. As one leaves the screen, it's recycled and refilled with the next item's data. Way cheaper than 500 plates on the table.

Code examples

Entity + DAO + Database

kotlin
@Entity(tableName = "todos")
data class Todo(
    @PrimaryKey(autoGenerate = true) val id: Long = 0,
    val title: String,
    val dueAt: Long,
    val done: Boolean = false
)

@Dao
interface TodoDao {
    @Insert suspend fun insert(t: Todo): Long
    @Update suspend fun update(t: Todo)
    @Delete suspend fun delete(t: Todo)
    @Query("SELECT * FROM todos ORDER BY dueAt")
    fun observeAll(): LiveData<List<Todo>>
}

@Database(entities = [Todo::class], version = 1)
abstract class AppDb : RoomDatabase() {
    abstract fun todoDao(): TodoDao
}

ListAdapter with DiffUtil

kotlin
class TodoAdapter : ListAdapter<Todo, TodoVH>(DIFF) {
    companion object {
        val DIFF = object : DiffUtil.ItemCallback<Todo>() {
            override fun areItemsTheSame(a: Todo, b: Todo) = a.id == b.id
            override fun areContentsTheSame(a: Todo, b: Todo) = a == b
        }
    }
    override fun onCreateViewHolder(p: ViewGroup, v: Int): TodoVH =
        TodoVH(ItemTodoBinding.inflate(LayoutInflater.from(p.context), p, false))

    override fun onBindViewHolder(h: TodoVH, pos: Int) = h.bind(getItem(pos))
}

Hands-on checklist

Milestone

ToDo app that saves & lists items persistently (Add + List working).

Common pitfalls

  • Doing DB work on the main thread — use suspend or LiveData/Flow.
  • Forgetting DiffUtil — you get flicker & lost scroll position.