Persistencia de Datos con Room en Android

Clase 16 de 19Curso de Jetpack Compose en Android

Resumen

¿Cómo solucionar la pérdida de datos al cerrar una aplicación?

En ocasiones, al cerrar nuestra aplicación o al ser destruida por el sistema operativo, los datos almacenados se pierden. Esto se debe a que estamos utilizando un data source local falso que reinicia todo a un estado anterior. Para solventar esto, podemos emplear una base de datos persistente, como lo es Room, que es comúnmente utilizada en aplicaciones Android.

¿Cómo configurar Room en tu proyecto?

Para comenzar, necesitamos configurar Room en nuestro archivo build.gradle. Aquí se encuentran varios comentarios que debemos eliminar para activar Room en nuestro proyecto:

  1. Elimina los comentarios del alias y la configuración de Room.
  2. En el archivo build.gradle a nivel de proyecto, descomenta KSP y Room.
  3. Sincroniza los cambios.

Una vez realizado lo anterior, podemos empezar a construir nuestra base de datos.

¿Cómo modelar tus entidades y base de datos con Room?

Room requiere que creemos entidades y una base de datos. Vamos a proceder con la creación de una entidad para nuestras tareas:

@Entity(tableName = "tasks")
data class TaskEntity(
    @PrimaryKey val id: String = "",
    val title: String,
    val description: String? = null,
    @ColumnInfo(name = "is_completed") val isCompleted: Boolean = false,
    val date: Long
)

Esta entidad TaskEntity representa la tabla que tendremos en nuestra base de datos.

Adicionalmente, debes crear la clase TodoDatabase que extienda de RoomDatabase y defina tu entidad.

¿Qué es un DAO y cómo interactúa con la base de datos?

Un DAO (Data Access Object) es fundamental para realizar operaciones en la base de datos. Debemos crear un DAO que defina estas operaciones:

@Dao
interface TaskDao {
    @Query("SELECT * FROM tasks")
    fun getAllTasks(): List

    @Query("SELECT * FROM tasks WHERE id = :id")
    fun getTaskById(id: String): TaskEntity?

    @Insert(onConflict = OnConflictStrategy.REPLACE)
    fun upsert(taskEntity: TaskEntity)

    @Delete
    fun deleteTask(taskEntity: TaskEntity)

    @Query("DELETE FROM tasks")
    fun deleteAllTasks()
}

¿Cómo implementar un DataSource local usando Room?

Para utilizar nuestra base de datos Room, necesitamos un RoomTaskLocalDataSource que implementará nuestra interfaz TaskLocalDataSource:

class RoomTaskLocalDataSource(
    private val taskDao: TaskDao,
    private val dispatcher: CoroutineDispatcher = Dispatchers.IO
) : TaskLocalDataSource {
    // Métodos para interactuar con la base de datos
}

Con esta clase, realizamos operaciones de inserción, actualización, eliminación, y obtención de datos de manera asíncrona, evitando bloquear el hilo principal.

¿Cómo inicializar y utilizar Room en tu aplicación Android?

Para asegurar el uso de Room en tu aplicación, es importante registrar la clase de aplicación en el manifiesto y crear un Singleton de la base de datos. Esto se hace mediante la configuración de una clase que extienda Application:

class TodoApplication : Application() {
    val database: TodoDatabase by lazy { TodoDatabase.getDatabase(this) }
}

¿Qué cambios son necesarios en los ViewModels?

Para que los ViewModels puedan utilizar Room, debemos proporcionarles el DataSource correcto. Esto implica inyectar dependencias manualmente y posteriormente automatizarlas mediante Dagger Hilt en otra clase:

val factory = HomeScreenViewModel.Factory(application)

Estos pasos son cruciales para gestionar correctamente las dependencias en el ciclo de vida de tu aplicación.

Finalmente, todo este set-up nos permite cargar, modificar y guardar datos de manera persistente en nuestra aplicación Android, mejorando significativamente la experiencia del usuario al prevenir la pérdida de datos al cerrar o reiniciar la app. A medida que avances, la inyección de dependencias te permitirá automatizar y simplificar este proceso manual.