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

Modelo de datos para una to do app
18:48 min - 7

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

HomeScreen con Scaffold en Jetpack Compose
23:16 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 con Room en Android
Resumen
Cuando una app Android se cierra o el sistema operativo la destruye, los datos en memoria desaparecen. Aprende a agregar persistencia de datos con Room en Android para que tus tareas sobrevivan al ciclo de vida de la aplicación, reemplazando un fake local data source por una base de datos real.
¿Por qué reemplazar un fake data source por Room?
Un fake local data source mantiene los datos solo en memoria. Cuando cierras la app o el sistema la destruye, todo vuelve al estado inicial. Room resuelve esto con una capa de abstracción sobre SQLite que persiste la información en disco.
¿Qué es Room en Android? Es la librería oficial de persistencia que envuelve SQLite y te permite trabajar con entidades, DAOs y bases de datos usando anotaciones de Kotlin [00:18].
Cómo activar Room en build.gradle
El primer paso es habilitar las dependencias en los archivos de configuración:
- Descomenta el alias y la configuración de Room en el
build.gradledel módulo. - Activa las librerías de Room para que queden disponibles.
- Descomenta KSP y Room en el
build.gradlea nivel de proyecto. - Sincroniza Gradle para que reconozca los cambios [00:50].
Una vez sincronizado, ya puedes empezar a crear los componentes principales.
¿Cómo crear una entidad y la base de datos en Room?
Una entidad representa una tabla. La base de datos agrupa entidades y expone los DAOs que harán las consultas. Ambos componentes son obligatorios para que Room funcione.
Cómo modelar un TaskEntity con anotaciones
Dentro del paquete data se crea la clase TaskEntity marcada con @Entity(tableName = "tasks"). Los campos clave del modelado son:
- id como
@PrimaryKey(autoGenerate = false)porque el ID se genera manualmente. - title como campo obligatorio y description como opcional.
- isCompleted mapeado con
@ColumnInfopara convertir camelCase a snake_case. - date almacenado como
Longporque Room no guarda tiposDatesin un converter [02:30].
Para mantener todo simple, se usan funciones de conversión: una en el companion object que recibe un Task y devuelve un TaskEntity (transformando la fecha con ZoneId.systemDefault() y toEpochMilli()), y una función de extensión toTask() que hace el camino inverso usando LocalDateTime.ofInstant() [03:40].
Cómo definir el TaskDao con queries SQLite
El DAO (Data Access Object) es la interfaz que expone las operaciones sobre la tabla. Se marca con @Dao e incluye queries como:
SELECT * FROM taskspara obtener todas las tareas.SELECT * FROM tasks WHERE id = :idpara buscar una tarea específica.@Upsertpara crear o actualizar (combinación de update + insert).- Operaciones de borrado por ID y de toda la tabla [05:10].
La clase TodoDatabase extiende de RoomDatabase, se anota con @Database(entities = [TaskEntity::class], version = 1) y expone el DAO. Dentro del companion object se implementa un singleton con una variable volatile para garantizar una única instancia, construida con Room.databaseBuilder() que recibe contexto, clase y nombre [06:30].
¿Cómo conectar Room con los ViewModels usando inyección manual?
La parte interesante llega cuando los ViewModels necesitan dependencias. Como aún no se usa Hilt, hay que cablear todo manualmente, y este paso prepara el terreno para la siguiente clase.
Por qué crear un RoomTaskLocalDataSource
Esta clase implementa la interfaz TaskLocalDataSource y recibe un taskDao y un CoroutineDispatcher. Las operaciones de base de datos no se pueden hacer en el hilo principal, así que cada función usa withContext(Dispatchers.IO) para mover el trabajo al background [08:15].
Las operaciones se homologan una a una respecto al fake: getAllTasks mapea entidades a modelos con flowOn(Dispatchers.IO), mientras que agregar, actualizar y borrar se envuelven en withContext con las transformaciones fromTask y toTask correspondientes.
¿Por qué usar Dispatchers.IO en operaciones de Room? Las consultas a base de datos son operaciones bloqueantes. Ejecutarlas en el hilo principal congela la UI;
Dispatchers.IOlas mueve a un pool optimizado para entrada y salida.
Cómo configurar TodoApplication y DataSourceFactory
Para compartir el DataSource y el Dispatcher de forma global, se crea una clase TodoApplication que hereda de Application. Dentro expone dos propiedades:
dispatcherIO: CoroutineDispatcherconDispatchers.IOpor defecto.dataSource: TaskLocalDataSourceobtenido desde unDataSourceFactory.createDataSource(context, dispatcher).
El DataSourceFactory es un object que instancia TodoDatabase.getDatabase(context) y devuelve un RoomTaskLocalDataSource con el DAO y el dispatcher. Después hay que registrar la clase en el AndroidManifest.xml con android:name=".TodoApplication" [11:45].
Dentro de cada ViewModel, el companion object expone una factory tipo viewModelFactory que recibe SavedStateHandle y el TaskLocalDataSource desde la application. En NavigationRoot, el ViewModel se crea pasando HomeScreenViewModel.Factory o TaskViewModelFactory según corresponda.
¿Funciona la persistencia al cerrar la aplicación?
Al ejecutar la app, la lista aparece vacía. Al crear tarea uno marcada como completada y tarea dos como incompleta, ambas se guardan. Al cerrar la app y volver a abrirla, la información persiste.
El cambio es enorme: pasaste de una fuente volátil a una que sobrevive al ciclo de vida del proceso. El costo fue toda la inicialización manual en los ViewModels, factorías y la clase application. ¿Te animas a probarlo en tu propio proyecto antes de migrar a Hilt? Cuéntanos en los comentarios cómo te fue con la conversión de tu fake data source.