Implementación de LocalStorage con Room y Realm en Android Studio
Clase 9 de 19 • Curso de Android: Modo Offline con Room y Realm
Resumen
La gestión de datos locales es un componente fundamental en el desarrollo de aplicaciones Android modernas. Dominar técnicas de almacenamiento local como Room y Realm permite crear experiencias de usuario fluidas incluso sin conexión a internet, mejorando significativamente el rendimiento y la usabilidad de nuestras aplicaciones.
¿Cómo implementar un sistema de almacenamiento local en Android?
El almacenamiento local en aplicaciones Android es esencial para mantener datos persistentes y permitir que la aplicación funcione sin conexión. En esta implementación, nos centraremos en crear una clase de almacenamiento local que utilice tanto Room como Realm para gestionar órdenes y pre-órdenes.
Para comenzar, necesitamos crear una clase LocalDataStorage
dentro de la carpeta de datos de nuestro proyecto. Esta clase será responsable de interactuar con nuestras bases de datos locales.
class LocalDataStorage @Inject constructor(
private val orderDao: OrderDao,
private val preOrderDao: PreOrderDao,
private val orderReal: OrderReal,
private val preOrderReal: PreOrderReal
) {
// Implementación de métodos
}
Observa que estamos utilizando la anotación @Inject
para que Hilt pueda inyectar esta clase en el grafo de dependencias. El constructor recibe cuatro parámetros:
orderDao
: Para interactuar con órdenes en RoompreOrderDao
: Para interactuar con pre-órdenes en RoomorderReal
: Para interactuar con órdenes en RealmpreOrderReal
: Para interactuar con pre-órdenes en Realm
¿Cuáles son las diferencias entre Room y Realm para el almacenamiento local?
Room y Realm son dos soluciones populares para el almacenamiento local en Android, pero tienen enfoques diferentes:
- Room: Es parte de Android Jetpack y utiliza SQLite como base. Trabaja con entidades marcadas con anotaciones específicas y requiere DAOs (Data Access Objects) para interactuar con la base de datos.
- Realm: Es una base de datos orientada a objetos que permite trabajar directamente con objetos Kotlin/Java sin necesidad de mapeo.
La principal diferencia radica en cómo se estructuran y acceden a los datos. Room utiliza entidades y DAOs, mientras que Realm trabaja directamente con objetos.
¿Cómo implementar operaciones CRUD para órdenes usando Room?
Vamos a implementar las operaciones básicas para gestionar órdenes utilizando Room:
// Orders with Room
suspend fun insertOrdersRoom(orders: List<OrderEntity>) {
orderDao.insertOrders(orders)
}
fun getOrdersRoom(): Flow<List<OrderEntity>> {
return orderDao.getOrders()
}
suspend fun getOrderByIdRoom(id: String): OrderEntity {
return orderDao.getOrderById(id)
}
Estas funciones nos permiten:
- Insertar una lista de órdenes en la base de datos
- Obtener todas las órdenes como un Flow (flujo observable)
- Obtener una orden específica por su ID
Es importante notar que estamos utilizando funciones suspendidas (suspend functions) para operaciones que pueden tomar tiempo, como insertar o buscar datos específicos. Esto es una práctica recomendada en Kotlin para operaciones asíncronas.
¿Cómo implementar operaciones CRUD para órdenes usando Realm?
De manera similar, implementamos las operaciones para Realm:
// Orders with Realm
suspend fun insertOrdersRealm(orders: List<OrderObject>) {
orderReal.insertOrders(orders)
}
fun getOrdersRealm(): Flow<List<OrderObject>> {
return orderReal.getOrders()
}
suspend fun getOrderByIdRealm(id: String): Result<OrderObject?> {
return runCatching {
orderReal.getOrderById(id)
}
}
Observa que para la operación de obtener una orden por ID en Realm, estamos utilizando runCatching
para manejar posibles excepciones y devolver un objeto Result
. Esto proporciona una forma más segura de manejar errores.
¿Cómo gestionar pre-órdenes en bases de datos locales?
Las pre-órdenes representan órdenes que aún no se han sincronizado con el servidor. Necesitamos implementar métodos para:
- Guardar pre-órdenes localmente
- Recuperar pre-órdenes almacenadas
- Eliminar pre-órdenes específicas
- Actualizar el estado de sincronización
Implementación para Room:
// PreOrders with Room
suspend fun savePreOrderRoom(preOrder: PreOrderEntity) {
preOrderDao.insertPreOrder(preOrder)
}
fun getPreOrdersRoom(): Flow<List<PreOrderEntity>> {
return preOrderDao.getPreOrders()
}
suspend fun deletePreOrderByIdRoom(id: Long) {
preOrderDao.deletePreOrderById(id)
}
suspend fun retryPreOrderSyncRoom(id: Long, isSynced: Boolean) {
preOrderDao.updatePreOrderSyncStatus(id, isSynced)
}
Implementación para Realm:
// PreOrders with Realm
suspend fun savePreOrderRealm(preOrder: PreOrderObject) {
preOrderReal.insertPreOrder(preOrder)
}
fun getPreOrdersRealm(): Flow<List<PreOrderObject>> {
return preOrderReal.getPreOrders()
}
suspend fun deletePreOrderByIdRealm(id: Long) {
preOrderReal.deletePreOrderById(id)
}
suspend fun retryPreOrderSyncRealm(id: Long, isSynced: Boolean) {
preOrderReal.updatePreOrderSyncStatus(id, isSynced)
}
Un aspecto importante a considerar es que los IDs de las pre-órdenes son de tipo Long
, mientras que los IDs de las órdenes son de tipo String
. Esto se debe a que las pre-órdenes se generan localmente y utilizan un identificador numérico, mientras que las órdenes sincronizadas con el servidor pueden tener identificadores en formato de cadena.
El almacenamiento local es fundamental para crear aplicaciones robustas que funcionen incluso sin conexión a internet. Tanto Room como Realm ofrecen soluciones potentes para este propósito, cada una con sus ventajas. ¿Has implementado alguna de estas soluciones en tus proyectos? Comparte tu experiencia en los comentarios.