Integración de Real en Android: Dependencias y Objetos de Datos

Clase 7 de 19Curso de Android: Modo Offline con Room y Realm

Resumen

La integración de bases de datos en aplicaciones Android es fundamental para crear experiencias de usuario robustas y funcionales. En este artículo, exploraremos cómo implementar Realm, una potente alternativa a Room, para gestionar datos locales en aplicaciones Kotlin. Aprenderás las diferencias clave entre ambas tecnologías y cómo configurar correctamente los objetos y consultas en Realm.

¿Cómo configurar Realm en un proyecto Android?

Antes de comenzar a utilizar Realm en nuestro proyecto, necesitamos agregar las dependencias necesarias. Este proceso es similar a la configuración de Room, pero con algunas diferencias importantes.

Agregando las dependencias de Realm

Para integrar Realm en nuestro proyecto Android, debemos seguir estos pasos:

  1. Agregar la versión de Realm en el archivo de configuración:

    // Debajo de Room
    // Versión 2.1.0 - compatible con Kotlin 2.0
    
  2. Incluir el plugin de Realm para Kotlin:

    // En la sección de plugins
    id 'io.realm.kotlin'
    
  3. Agregar las dependencias en el módulo de la aplicación:

    // Implementación de Realm
    implementation 'io.realm.kotlin:library-base:2.1.0'
    

Es importante destacar que estamos utilizando la versión 2.1.0 de Realm, que es compatible con Kotlin 2.0. Esta compatibilidad es crucial para evitar problemas de integración en proyectos modernos.

Organizando el proyecto

Para mantener una estructura clara, es recomendable organizar nuestro código en carpetas específicas:

  • Crear una carpeta room para todo lo relacionado con Room
  • Crear una carpeta realm para los componentes de Realm

Esta organización nos permitirá mantener una clara separación de responsabilidades y facilitará el mantenimiento del código a largo plazo.

¿Cuáles son las diferencias entre entidades de Room y objetos de Realm?

Una de las diferencias fundamentales entre Room y Realm está en cómo se definen las entidades o modelos de datos.

Creando objetos en Realm vs entidades en Room

En Room:

@Entity(tableName = "orders")
data class OrderEntity(
    @PrimaryKey(autoGenerate = true)
    val id: Long = 0,
    val customerName: String,
    val item: String,
    val total: Double,
    val imageUrl: String
)

En Realm:

@RealmClass
open class OrderObject : RealmObject {
    @PrimaryKey
    var id: String = ""
    var customerName: String = ""
    var item: String = ""
    var total: Double = 0.0
    var imageUrl: String = ""
}

Las diferencias clave son:

  • Room utiliza data class mientras que Realm usa open class que extiende de RealmObject
  • En Realm es obligatorio inicializar todos los atributos
  • Room permite autogenerar IDs con autoGenerate = true, mientras que Realm no ofrece esta funcionalidad
  • Los objetos de Realm requieren la anotación @RealmClass

Ejemplo de PreorderObject en Realm

@RealmClass
open class PreorderObject : RealmObject {
    @PrimaryKey
    var id: Long = 0
    var customerName: String = ""
    var item: String = ""
    var isSent: Boolean = false
}

¿Cómo implementar operaciones CRUD con Realm?

Al igual que con Room, necesitamos crear clases que manejen las operaciones de base de datos, pero la sintaxis y el enfoque son diferentes.

Implementando OrderReal (equivalente a OrderDao)

class OrderReal @Inject constructor(
    private val realm: Realm
) {
    suspend fun insertOrders(orders: List<OrderObject>) {
        realm.write {
            orders.forEach { order ->
                copyToRealm(order)
            }
        }
    }

    fun getOrders(): Flow<List<OrderObject>> {
        return realm.query(OrderObject::class)
            .find()
            .asFlow()
            .map { it.list }
    }

    suspend fun getOrder(id: String): OrderObject? {
        return realm.query(OrderObject::class, "id == $0", id)
            .first()
            .find()
    }
}

Observemos las diferencias con Room:

  • En Realm usamos write para operaciones de escritura en lugar de anotaciones como @Insert
  • Para insertar múltiples elementos, debemos iterar y usar copyToRealm para cada objeto
  • Las consultas se realizan con query() en lugar de métodos anotados con @Query
  • Realm devuelve resultados que deben convertirse a Flow con asFlow()

Implementando PreorderReal

class PreorderReal @Inject constructor(
    private val realm: Realm
) {
    suspend fun insertPreOrder(preorderObject: PreorderObject) {
        realm.write {
            copyToRealm(preorderObject)
        }
    }

    fun getPreorders(): Flow<List<PreorderObject>> {
        return realm.query(PreorderObject::class)
            .asFlow()
            .map { it.list }
    }

    suspend fun deletePreorder(id: Long) {
        realm.write {
            query(PreorderObject::class, "id == $0", id)
                .first()
                .find()
                ?.let { delete(it) }
        }
    }

    suspend fun updateIsSent(id: Long, isSent: Boolean) {
        realm.write {
            query(PreorderObject::class, "id == $0", id)
                .first()
                .find()
                ?.isSent = isSent
        }
    }
}

¿Cómo configurar la inyección de dependencias para Realm?

Para integrar Realm con Hilt (sistema de inyección de dependencias), necesitamos crear un módulo específico.

@Module
@InstallIn(SingletonComponent::class)
object RealmModule {
    
    @Provides
    @Singleton
    fun provideRealmConfiguration(): RealmConfiguration {
        return RealmConfiguration.Builder(
            schema = setOf(
                PreorderObject::class,
                OrderObject::class
            )
        )
            .name("realm-database.db")
            .schemaVersion(1)
            .build()
    }
    
    @Provides
    @Singleton
    fun provideRealm(configuration: RealmConfiguration): Realm {
        return Realm.open(configuration)
    }
}

Este módulo:

  1. Proporciona una configuración de Realm que especifica:

    • Las clases de esquema (nuestros objetos)
    • El nombre de la base de datos
    • La versión del esquema
  2. Crea y proporciona una instancia de Realm utilizando la configuración anterior

La configuración es similar a la de Room, pero con la sintaxis específica de Realm.

La implementación de Realm ofrece una alternativa interesante a Room para el almacenamiento local en Android. Aunque ambas tecnologías cumplen propósitos similares, Realm se enfoca más en un modelo orientado a objetos mientras que Room está basado en SQL. Elegir entre una u otra dependerá de las necesidades específicas de tu proyecto y tu familiaridad con cada tecnología.

¿Has trabajado con Realm anteriormente? ¿Qué ventajas o desventajas has encontrado en comparación con Room? Comparte tu experiencia en los comentarios.