Contenido del curso
Personalización de la UI con Material Design
Composición de layouts en Jetpack Compose
- 6

Modelado de Tareas y Operaciones CRUD en Kotlin
18:48 min - 7

Creación de Componentes UI en Jetpack Compose: Summary y Task Item
17:36 min - 8

Creación de una Pantalla con Componentes en Jetpack Compose
23:17 min - 9

Creación de un Arco de Progreso Animado en Compose
14:39 min - 10

Gestión de Acciones y Estados en ViewModel de Android
23:14 min - 11

Formateo y Ajustes Finales en Pantalla Home de To Do App
04:17 min
Construcción de funcionalidades
Navegación en Jetpack Compose
Creación de Bases de datos y dependencias
Finalización de la app
Persistencia de Datos con Room en Android
Resumen
Cuando cierras una aplicación Android y el sistema operativo la destruye, toda la información almacenada en memoria se pierde. Las tareas, los estados, todo vuelve a un punto inicial. Room es la solución más utilizada para resolver este problema, permitiendo guardar datos de forma persistente en una base de datos local SQLite. A continuación se detalla paso a paso cómo migrar de un fake local data source a uno real con Room, incluyendo la creación de entidades, DAOs y la inyección manual de dependencias.
¿Cómo se configura Room en el proyecto?
El primer paso es activar las dependencias necesarias en los archivos de configuración. En el archivo build.gradle a nivel de módulo, se descomentaron las líneas correspondientes a KSP y las librerías de Room [0:30]. Lo mismo se hizo en el build.gradle a nivel de proyecto, habilitando los plugins de KSP y Room. Tras sincronizar el proyecto, las dependencias quedan listas para usar.
Dentro de la carpeta data, se crea la clase TodoDatabase, que es una abstract class que extiende de RoomDatabase [1:07]. Esta clase se marca con la anotación @Database, indicando las entidades que contiene y la versión de la base de datos.
¿Qué son las entidades y el DAO en Room?
Una entidad en Room representa una tabla dentro de la base de datos. Se creó TaskEntity marcada con la anotación @Entity y un table name de "tasks" [1:24]. El modelado incluye:
- Un campo
idmarcado como@PrimaryKeyconautoGenerate = false, ya que el identificador se genera externamente. - Un
titleobligatorio. - Una
descriptionopcional. - Un campo
isCompleted, convertido de camelCase a snake_case usando@ColumnInfo(name = "is_completed"). - Un campo
datede tipoLong, ya que Room no soporta directamente objetosDatesin un type converter [2:12].
Para la conversión entre modelos se crearon dos funciones. La función fromTask dentro de un companion object transforma un Task en TaskEntity, convirtiendo la fecha a epoch millis mediante ZoneId.systemDefault() y toEpochMilli() [2:40]. La función inversa toTask() reconstruye un Task usando Instant.ofEpochMilli() y ZoneId.systemDefault() [3:16].
El DAO (Data Access Object) es una interfaz marcada con @Dao que define las operaciones sobre la tabla [3:52]. Las operaciones implementadas fueron:
getAllTasks: query de selección completa.getTaskById: búsqueda por identificador.upsert: operación que funciona como update o insert, útil para crear o actualizar tareas.deleteTaskById: eliminación por ID.deleteAllTasks: borrado completo de la tabla.
¿Cómo se implementa el data source real y la inyección manual?
Se creó RoomTaskLocalDataSource, que recibe un TaskDao y un dispatcher para ejecutar operaciones en un hilo de background [5:00]. Esto es fundamental: las operaciones de base de datos no pueden ejecutarse en el hilo principal (UI). Cada función utiliza withContext(Dispatchers.IO) para cambiar al hilo adecuado.
getAllTasksmapea las entidades a modelos de dominio conflowOn(Dispatchers.IO).addTaskyupdateTaskconvierten el modelo conTaskEntity.fromTask()antes de insertar.removeTaskyremoveAllTasksinvocan las funciones de borrado del DAO.
¿Cómo se crean los ViewModels con dependencias manuales?
Sin un framework de inyección de dependencias, la construcción de ViewModels con dependencias requiere un companion object con una variable factory [7:08]. Este factory le indica al ViewModel cómo instanciarse con las dependencias necesarias: un SavedStateHandle y el TaskLocalDataSource.
Se creó una clase TodoApplication que hereda de Application [8:00]. Esta clase global contiene las variables de dispatcher (Dispatchers.IO) y el data source, construido mediante un DataSourceFactory. Este factory invoca TodoDatabase.getDatabase(context) para obtener la instancia de la base de datos como Singleton usando una variable @Volatile [6:15].
¿Qué cambios se hacen en la navegación?
Los ViewModels se extrajeron de las pantallas individuales y se movieron al nivel de navegación en NavigationRoot [10:36]. Allí se crean con su factory correspondiente y se pasan como parámetro a cada pantalla. Este patrón prepara el terreno para la inyección de dependencias con Hilt en la siguiente clase.
Al ejecutar la aplicación, la lista aparece vacía inicialmente. Pero al crear tareas y cerrar la aplicación, la información persiste al reabrirla [11:28]. El cambio de una fuente de datos volátil a una persistente tiene un costo: toda la inicialización manual de dependencias. Hilt simplificará ese proceso significativamente.
¿Has implementado Room en tus proyectos? Comparte tu experiencia o dudas sobre la migración de datos volátiles a persistentes.