Room and LiveData
One “out of the box” option is to use LiveData
.
LiveData
in the "Thinking About Threads and LiveData" chapter of Elements of Android Jetpack!
No special dependencies are required, other than Room itself. You can simply wrap your DAO @Query
return values in LiveData
:
package com.commonsware.todo.repo
import androidx.lifecycle.LiveData
import androidx.room.*
import kotlinx.coroutines.flow.Flow
import org.threeten.bp.Instant
import java.util.*
@Entity(tableName = "todos", indices = [Index(value = ["id"])])
data class ToDoEntity(
val description: String,
@PrimaryKey
val id: String = UUID.randomUUID().toString(),
val notes: String = "",
val createdOn: Instant = Instant.now(),
val isCompleted: Boolean = false
) {
constructor(model: ToDoModel) : this(
id = model.id,
description = model.description,
isCompleted = model.isCompleted,
notes = model.notes,
createdOn = model.createdOn
)
fun toModel(): ToDoModel {
return ToDoModel(
id = id,
description = description,
isCompleted = isCompleted,
notes = notes,
createdOn = createdOn
)
}
@Dao
interface Store {
@Query("SELECT * FROM todos")
fun all(): LiveData<List<ToDoEntity>>
@Query("SELECT * FROM todos WHERE id = :modelId")
fun find(modelId: String): LiveData<ToDoEntity?>
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun save(vararg entities: ToDoEntity)
@Delete
fun delete(vararg entities: ToDoEntity)
}
}
This ToDoEntity
class is from the LiveData
module of the book’s primary sample project.
We have a @Dao
-annotated interface named ToDoEntity.Store
. It has four functions, two of which have @Query
annotations (all()
and find()
). Instead of returning entities directly, though, they return LiveData
wrappers around those entities.
When we call all()
or find()
, we get a LiveData
back immediately. The I/O will not be performed until we observe()
that LiveData
. At that point, the database I/O will be conducted on a Room-supplied background thread, but the results will be delivered to our Observer
on the main application thread (as with all uses of LiveData
).
Benefits of LiveData
As with all of our reactive options, the database I/O gets offloaded to a background thread, so we do not need to fork such a thread ourselves. Yet, we still get the results delivered to us on the main application thread, so we can easily apply those results to our UI.
Also, LiveData
does not require any additional dependencies, which can help to keep the app a bit smaller.
Issues with LiveData
LiveData
is not an option for @Insert
, @Update
, or @Delete
functions — you would still need to manage your own background threads for those.
LiveData
always delivers its results on the main application thread. This is great when we want those results on the main application thread. This is a problem when we do not want those results on the main application thread, such as performing some database I/O in preparation for making a Web service request.
LiveData
needs to be observed, and the typical way of observing a LiveData
requires that you pass a LifecycleOwner
to observe()
. This is annoying when you do not have a LifecycleOwner
to use, such as when using LiveData
in some types of services. You can use observeForever()
to avoid the need for the LifecycleOwner
, but then you need to remember to remove your Observer
, lest you accidentally wind up with a memory leak.
And LiveData
is lightweight by design. If you have complex background operations to perform, such as combining results from multiple sources and converting those results into specific object types, LiveData
has limited facilities to help with that. And, what it does have can be somewhat arcane to use (e.g., MediatorLiveData
).
Prev Table of Contents Next
This book is licensed under the Creative Commons Attribution-ShareAlike 4.0 International license.