Persistencia con Room (MVVM + Jetpack Compose)¶
En los apartados anteriores se ha trabajado con SQLite nativo mediante SQLiteOpenHelper.
En aplicaciones Android modernas (2025), el enfoque recomendado es utilizar Room, una librería de AndroidX que actúa como una capa de abstracción sobre SQLite, proporcionando:
- Seguridad en las consultas (verificación en tiempo de compilación).
- Menos código repetitivo.
- Integración directa con MVVM, coroutines y Flow.
- Mejor mantenimiento y escalabilidad.
Room forma parte de la arquitectura recomendada por Google para Android.
Arquitectura utilizada: MVVM¶
En este documento se trabajará con el patrón MVVM (Model–View–ViewModel):
- Model → Entidades Room + DAO.
- ViewModel → Lógica de negocio y estado de la UI.
- View → Interfaz creada con Jetpack Compose.
- Repository → Capa intermedia entre ViewModel y Room.
Este enfoque:
- Separa responsabilidades.
- Facilita pruebas.
- Escala mejor en aplicaciones reales.
Dependencias necesarias¶
implementation "androidx.room:room-runtime:2.6.1"
kapt "androidx.room:room-compiler:2.6.1"
implementation "androidx.room:room-ktx:2.6.1"
implementation "androidx.lifecycle:lifecycle-viewmodel-compose:2.8.6"
implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.8.6"
implementation "androidx.compose.material3:material3"
implementation "androidx.activity:activity-compose"
1. Entidad (Entity)¶
Representa una tabla de la base de datos.
import androidx.room.Entity
import androidx.room.PrimaryKey
@Entity(tableName = "articulos")
data class Articulo(
@PrimaryKey val codigo: Int,
val descripcion: String,
val precio: Double
)
2. DAO (Data Access Object)¶
Define las operaciones permitidas sobre la base de datos.
import androidx.room.*
import kotlinx.coroutines.flow.Flow
@Dao
interface ArticuloDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertar(articulo: Articulo)
@Query("SELECT * FROM articulos ORDER BY descripcion ASC")
fun obtenerTodos(): Flow<List<Articulo>>
@Query("SELECT * FROM articulos WHERE codigo = :codigo")
suspend fun obtenerPorCodigo(codigo: Int): Articulo?
@Delete
suspend fun borrar(articulo: Articulo)
@Query("DELETE FROM articulos WHERE codigo = :codigo")
suspend fun borrarPorCodigo(codigo: Int)
}
3. Base de datos Room¶
import androidx.room.Database
import androidx.room.RoomDatabase
@Database(
entities = [Articulo::class],
version = 1,
exportSchema = false
)
abstract class AppDatabase : RoomDatabase() {
abstract fun articuloDao(): ArticuloDao
}
4. Repository¶
Centraliza el acceso a los datos.
class ArticuloRepository(private val dao: ArticuloDao) {
val articulos = dao.obtenerTodos()
suspend fun insertar(articulo: Articulo) {
dao.insertar(articulo)
}
suspend fun borrarPorCodigo(codigo: Int) {
dao.borrarPorCodigo(codigo)
}
}
5. ViewModel¶
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.launch
class ArticuloViewModel(
private val repository: ArticuloRepository
) : ViewModel() {
val articulos = repository.articulos
fun insertar(codigo: Int, descripcion: String, precio: Double) {
viewModelScope.launch {
repository.insertar(
Articulo(codigo, descripcion, precio)
)
}
}
fun borrar(codigo: Int) {
viewModelScope.launch {
repository.borrarPorCodigo(codigo)
}
}
}
6. Interfaz con Jetpack Compose¶
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.foundation.layout.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
@Composable
fun ArticulosScreen(viewModel: ArticuloViewModel) {
val articulos by viewModel.articulos.collectAsStateWithLifecycle(emptyList())
Column(Modifier.padding(16.dp)) {
articulos.forEach {
Text("${it.codigo} - ${it.descripcion} (${it.precio} €)")
}
Spacer(Modifier.height(16.dp))
Button(onClick = {
viewModel.insertar(1, "Producto ejemplo", 9.99)
}) {
Text("Añadir artículo")
}
}
}
Actividades¶
-
AC 605 (RA3 / CE3a / IC2 / 1p). Diseña una aplicación con Room que almacene una lista de tareas pendientes. Cada tarea debe tener un identificador, un título y un estado de completada. La interfaz se implementará con Jetpack Compose y los datos se gestionarán mediante un ViewModel.
-
AC 606 (RA3 / CE3b / IC2 / 1p). Crea una aplicación que permita registrar alumnos en una base de datos Room. Cada alumno tendrá un código, nombre y nota media. La lista de alumnos se mostrará automáticamente al insertar o borrar registros, utilizando Flow y Compose.
-
AC 607 (RA3 / CE3c / IC3 / 1p). Implementa una aplicación que gestione productos mediante Room. El usuario podrá añadir y eliminar productos desde la interfaz Compose, aplicando correctamente el patrón MVVM.
-
PR 608 (RA4 / CE4a / IC3 / 4p). Desarrolla una aplicación de gestión de artículos utilizando Room, MVVM y Jetpack Compose. La app debe permitir insertar, listar y borrar artículos, mostrar la lista en tiempo real mediante Flow y separar correctamente las capas de entidad, DAO, repositorio y ViewModel.
-
PR 609 (RA4 / CE4b / IC4 / 5p). Crea una aplicación completa de inventario con Room y Jetpack Compose. La aplicación deberá permitir altas, bajas y edición de elementos, usar navegación entre pantallas, aplicar MVVM de forma estricta y mantener el estado de la interfaz durante cambios de configuración.