Docs Menu
Docs Home
/ /
Realm

Reaccionar a los cambios - SDK de Kotlin

Cualquier aplicación moderna debería poder reaccionar ante cambios en los datos, independientemente de su origen. Cuando un usuario añade un elemento a una lista, es posible que quieras actualizar la interfaz de usuario, mostrar una notificación o registrar un mensaje. Cuando alguien actualiza ese elemento, es posible que quieras cambiar su estado visual o enviar una solicitud de red. Finalmente, cuando alguien elimina el elemento, probablemente quieras eliminarlo de la interfaz de usuario. El sistema de notificaciones de Realm te permite observar y reaccionar ante los cambios en tus datos, independientemente de las escrituras que los provocaron.

La arquitectura fija del SDK de Kotlin hace que las notificaciones sean aún más importantes. Dado que el SDK de Kotlin no tiene objetos activos que se actualicen automáticamente, usarás notificaciones para mantener sincronizadas la interfaz de usuario y la capa de datos.

Puedes suscribirte a los cambios en los siguientes eventos:

  • Consulta sobre la colección

  • Objeto de reino

  • Colecciones de reinos (por ejemplo, lista)

El SDK solo proporciona notificaciones para objetos anidados hasta en cuatro capas. Si necesita reaccionar a cambios en objetos anidados más profundos, registre un detector de cambios de ruta de clave. Para obtener más información, consulte "Registrar un detector de cambios de ruta de clave" en esta página.

También puedes reaccionar a los cambios en el estado de autenticación del usuario. Para obtener más información, consulta Observar cambios de autenticación.

Ejemplo

Acerca de los ejemplos de esta página

Los ejemplos de esta página utilizan dos tipos de objetos Realm, Character y Fellowship:

class Character(): RealmObject {
@PrimaryKey
var name: String = ""
var species: String = ""
var age: Int = 0
constructor(name: String, species: String, age: Int) : this() {
this.name = name
this.species = species
this.age = age
}
}
class Fellowship() : RealmObject {
@PrimaryKey
var name: String = ""
var members: RealmList<Character> = realmListOf()
constructor(name: String, members: RealmList<Character>) : this() {
this.name = name
this.members = members
}
}

Los ejemplos tienen estos datos de muestra:

val config = RealmConfiguration.Builder(setOf(Fellowship::class, Character::class))
.name(realmName)
.build()
val realm = Realm.open(config)
val frodo = Character("Frodo", "Hobbit", 51)
val samwise = Character("Samwise", "Hobbit", 39)
val aragorn = Character("Aragorn", "Dúnedain", 87)
val legolas = Character("Legolas", "Elf", 2931)
val gimli = Character("Gimli", "Dwarf", 140)
val gollum = Character("Gollum", "Hobbit", 589)
val fellowshipOfTheRing = Fellowship(
"Fellowship of the Ring",
realmListOf(frodo, samwise, aragorn, legolas, gimli))
realm.writeBlocking{
this.copyToRealm(fellowshipOfTheRing)
this.copyToRealm(gollum) // not in fellowship
}
realm.close()

Puedes registrar un controlador de notificaciones en cualquier consulta dentro de un dominio. Primero, crea un flujo Kotlin a partir de la consulta con asFlow(). A continuación, usa el collect() método para gestionar los eventos de ese flujo. Los eventos de tipo UpdatedResults registran todos los cambios en los objetos que coinciden con la consulta mediante las siguientes propiedades:

Propiedad

Tipo

Descripción

insertions

IntArray

Índices de la nueva colección que se agregaron en esta versión.

insertionRanges

Matriz<ListChangeSet.Range>

Rangos de índices en la nueva colección que se agregaron en esta versión.

changes

IntArray

Índices de los objetos de la nueva colección que fueron modificados en esta versión.

changeRanges

Matriz<ListChangeSet.Range>

Rangos de índices en la nueva colección que fueron modificados en esta versión.

deletions

IntArray

Índices de la versión anterior de la colección que se han eliminado de esta.

deletionRanges

Matriz<ListChangeSet.Range>

Rangos de índices de la versión anterior de la colección que se han eliminado de esta.

list

Resultados del reino<T as RealmObject>

Se monitorea la recopilación de resultados para detectar cambios.

// Listen for changes on whole collection
val characters = realm.query(Character::class)
// flow.collect() is blocking -- run it in a background context
val job = CoroutineScope(Dispatchers.Default).launch {
// create a Flow from that collection, then add a listener to the Flow
val charactersFlow = characters.asFlow()
val subscription = charactersFlow.collect { changes: ResultsChange<Character> ->
when (changes) {
// UpdatedResults means this change represents an update/insert/delete operation
is UpdatedResults -> {
changes.insertions // indexes of inserted objects
changes.insertionRanges // ranges of inserted objects
changes.changes // indexes of modified objects
changes.changeRanges // ranges of modified objects
changes.deletions // indexes of deleted objects
changes.deletionRanges // ranges of deleted objects
changes.list // the full collection of objects
}
else -> {
// types other than UpdatedResults are not changes -- ignore them
}
}
}
}
// Listen for changes on RealmResults
val hobbits = realm.query(Character::class, "species == 'Hobbit'")
val hobbitJob = CoroutineScope(Dispatchers.Default).launch {
val hobbitsFlow = hobbits.asFlow()
val hobbitsSubscription = hobbitsFlow.collect { changes: ResultsChange<Character> ->
// ... all the same data as above
}
}

Puede registrar un controlador de notificaciones en un objeto específico dentro de un dominio. Realm notifica a su controlador cuando cambia alguna de las propiedades del objeto. Para registrar un detector de cambios en un solo objeto, obtenga una consulta RealmSingleQuery realm.query.first()con. Genere un flujo a partir de esa consulta con asFlow(). El controlador recibe un SingleQueryChange objeto que comunica los cambios del objeto mediante los siguientes subtipos:

subtipo

Propiedades

notas

UpdatedObject

campos modificados, obj

Pase un nombre de campo a isFieldChanged() para verificar si ese campo cambió.

DeletedObject

obj

Dado que obj siempre refleja la última versión del objeto, siempre devuelve un valor nulo en este subtipo.

// query for the specific object you intend to listen to
val frodo = realm.query(Character::class, "name == 'Frodo'").first()
// flow.collect() is blocking -- run it in a background context
val job = CoroutineScope(Dispatchers.Default).launch {
val frodoFlow = frodo.asFlow()
frodoFlow.collect { changes: SingleQueryChange<Character> ->
when (changes) {
is UpdatedObject -> {
changes.changedFields // the changed properties
changes.obj // the object in its newest state
changes.isFieldChanged("name") // check if a specific field changed in value
}
is DeletedObject -> {
// if the object has been deleted
changes.obj // returns null for deleted objects -- always reflects newest state
}
is InitialObject -> {
// Initial event observed on a RealmObject or EmbeddedRealmObject flow.
// It contains a reference to the starting object state.
changes.obj
}
is PendingObject -> {
// Describes the initial state where a query result does not contain any elements.
changes.obj
}
}
}
}

Puedes registrar un controlador de notificaciones en RealmList, RealmSet o RealmMap. Realm notifica a tu controlador cuando cambia cualquier elemento de la colección. Primero, crea un flujo de Kotlin a partir de la colección con asFlow(). A continuación, usa el método collect() para gestionar los eventos de ese flujo. Los eventos de tipo ListChange, SetChange o MapChange registran todos los cambios en la colección.

RealmList Las notificaciones implementan la interfaz ListChange, que describe los posibles cambios que pueden ocurrir en una colección RealmList. Estos estados se representan mediante subclases:

  • InitialList

  • UpdatedList

  • DeletedList

Un ListChangeSet es una interfaz que modela los cambios que pueden ocurrir en una lista, y las propiedades incluyen:

Propiedad

Tipo

Descripción

insertions

IntArray

Índices de la nueva colección que se agregaron en esta versión.

insertionRanges

Matriz<ListChangeSet.Range>

Rangos de índices en la nueva colección que se agregaron en esta versión.

changes

IntArray

Índices de los objetos de la nueva colección que fueron modificados en esta versión.

changeRanges

Matriz<ListChangeSet.Range>

Rangos de índices en la nueva colección que fueron modificados en esta versión.

deletions

IntArray

Índices de la versión anterior de la colección que se han eliminado de esta.

deletionRanges

Matriz<ListChangeSet.Range>

Rangos de índices de la versión anterior de la colección que se han eliminado de esta.

list

Resultados del reino<T as RealmObject>

Se está monitorizando la recopilación de resultados para detectar cambios. Esto se proporciona cuando el tipo de evento es UpdatedList.

RealmSet Las notificaciones implementan la interfaz SetChange, que describe los posibles cambios que pueden ocurrir en una colección RealmSet. Estos estados se representan mediante subclases:

  • InitialSet

  • UpdatedSet

  • DeletedSet

Un SetChangeSet es una interfaz que modela los cambios que pueden ocurrir en un conjunto, y las propiedades incluyen:

Propiedad

Tipo

Descripción

deletions

Int

El número de entradas que se han eliminado en esta versión de la colección.

insertions

Int

El número de entradas que se han insertado en esta versión de la colección.

set

RealmSet<T>

El conjunto que se está monitoreando para detectar cambios. Esto se proporciona cuando el tipo de evento es UpdatedSet.

RealmMap Las notificaciones implementan la interfaz MapChange, que describe los posibles cambios que pueden ocurrir en una colección RealmMap. Estos estados se representan mediante subclases:

  • InitialMap

  • UpdatedMap

  • DeletedMap

Un MapChangeSet es una interfaz que modela los cambios que pueden ocurrir en un mapa, y las propiedades incluyen:

Propiedad

Tipo

Descripción

deletions

Matriz<K>

Las claves que se han eliminado en esta versión del mapa.

insertions

Matriz<K>

Las claves que se han insertado en esta versión del mapa.

changes

Matriz<K>

Las claves cuyos valores han cambiado en esta versión de la colección.

set

Mapa del reino<K, V>

El mapa que se está monitoreando para detectar cambios. Esto se proporciona cuando el tipo de evento es UpdatedMap.

// query for the specific object you intend to listen to
val fellowshipOfTheRing = realm.query(Fellowship::class, "name == 'Fellowship of the Ring'").first().find()!!
val members = fellowshipOfTheRing.members
// flow.collect() is blocking -- run it in a background context
val job = CoroutineScope(Dispatchers.Default).launch {
val membersFlow = members.asFlow()
membersFlow.collect { changes: ListChange<Character> ->
when (changes) {
is UpdatedList -> {
changes.insertions // indexes of inserted objects
changes.insertionRanges // ranges of inserted objects
changes.changes // indexes of modified objects
changes.changeRanges // ranges of modified objects
changes.deletions // indexes of deleted objects
changes.deletionRanges // ranges of deleted objects
changes.list // the full collection of objects
}
is DeletedList -> {
// if the list was deleted
}
is InitialList -> {
// Initial event observed on a RealmList flow. It contains a reference
// to the starting list state.
changes.list
}
}
}
}

Nuevo en la versión 1.13.0.

Cuando registra un controlador de notificaciones, puede pasar una lista opcional de nombres de propiedades de cadena para especificar la ruta o rutas de clave a observar.

Al especificar rutas de clave, solo los cambios en ellas activan bloques de notificación. Los demás cambios no activan bloques de notificación.

En el siguiente ejemplo, registramos un detector de cambio de ruta de clave para la propiedad age:

runBlocking {
// Query for the specific object you intend to listen to.
val frodoQuery = realm.query(Character::class, "name == 'Frodo'").first()
val observer = async {
val frodoFlow = frodoQuery.asFlow(listOf("age"))
frodoFlow.collect { changes: SingleQueryChange<Character> ->
// Change listener stuff in here.
}
}
// Changing a property whose key path you're not observing does not trigger a notification.
realm.writeBlocking {
findLatest(frodoObject)!!.species = "Ring Bearer"
}
// Changing a property whose key path you are observing triggers a notification.
realm.writeBlocking {
findLatest(frodoObject)!!.age = 52
}
// For this example, we send the object change to a Channel where we can verify the
// changes we expect. In your application code, you might use the notification to
// update the UI or take some other action based on your business logic.
channel.receiveOrFail().let { objChange ->
assertIs<UpdatedObject<*>>(objChange)
assertEquals(1, objChange.changedFields.size)
// Because we are observing only the `age` property, the change to
// the `species` property does not trigger a notification.
// The first notification we receive is a change to the `age` property.
assertEquals("age", objChange.changedFields.first())
}
observer.cancel()
channel.close()
}

Nota

Varios tokens de notificación en el mismo objeto que filtran por rutas de clave independientes no filtran de forma exclusiva. Si se cumple un cambio de ruta de clave para un token de notificación, se ejecutarán todos los bloques de tokens de notificación de ese objeto.

Puede usar la notación de puntos para observar rutas de claves anidadas. De forma predeterminada, el SDK solo notifica objetos anidados hasta en cuatro capas. Observe una ruta de clave específica si necesita observar cambios en objetos anidados con mayor profundidad.

En el siguiente ejemplo, buscamos actualizaciones en la propiedad anidada members.age. Tenga en cuenta que el SDK informa del cambio en la propiedad de nivel superior members, aunque estemos buscando cambios en una propiedad anidada:

runBlocking {
// Query for the specific object you intend to listen to.
val fellowshipQuery = realm.query(Fellowship::class).first()
val observer = async {
val fellowshipFlow = fellowshipQuery.asFlow(listOf("members.age"))
fellowshipFlow.collect { changes: SingleQueryChange<Fellowship> ->
// Change listener stuff in here.
}
}
// Changing a property whose nested key path you are observing triggers a notification.
val fellowship = fellowshipQuery.find()!!
realm.writeBlocking {
findLatest(fellowship)!!.members[0].age = 52
}
// For this example, we send the object change to a Channel where we can verify the
// changes we expect. In your application code, you might use the notification to
// update the UI or take some other action based on your business logic.
channel.receiveOrFail().let { objChange ->
assertIs<UpdatedObject<*>>(objChange)
assertEquals(1, objChange.changedFields.size)
// While you can watch for updates to a nested property, the notification
// only reports the change on the top-level property. In this case, there
// was a change to one of the elements in the `members` property, so `members`
// is what the notification reports - not `age`.
assertEquals("members", objChange.changedFields.first())
}
observer.cancel()
channel.close()
}

Puedes usar comodines (*) para observar cambios en todas las rutas clave al nivel del comodín.

En el siguiente ejemplo, utilizamos el comodín para detectar cambios en cualquier propiedad anidada a un nivel de profundidad dentro de la propiedad members. Según el modelo utilizado en este ejemplo, este detector de cambios informaría sobre los cambios en las propiedades age, species o name de cualquier miembro. Tenga en cuenta que el SDK informa sobre el cambio en la propiedad members de nivel superior, aunque estemos observando cambios en una propiedad anidada:

runBlocking {
// Query for the specific object you intend to listen to.
val fellowshipQuery = realm.query(Fellowship::class).first()
val observer = async {
// Use a wildcard to observe changes to any key path at the level of the wildcard.
val fellowshipFlow = fellowshipQuery.asFlow(listOf("members.*"))
fellowshipFlow.collect { changes: SingleQueryChange<Fellowship> ->
// Change listener stuff in here.
}
}
// Changing any property at the level of the key path wild card triggers a notification.
val fellowship = fellowshipQuery.find()!!
realm.writeBlocking {
findLatest(fellowship)!!.members[0].age = 52
}
// For this example, we send the object change to a Channel where we can verify the
// changes we expect. In your application code, you might use the notification to
// update the UI or take some other action based on your business logic.
channel.receiveOrFail().let { objChange ->
assertIs<UpdatedObject<*>>(objChange)
assertEquals(1, objChange.changedFields.size)
// While you can watch for updates to a nested property, the notification
// only reports the change on the top-level property. In this case, there
// was a change to one of the elements in the `members` property, so `members`
// is what the notification reports - not `age`.
assertEquals("members", objChange.changedFields.first())
}
observer.cancel()
channel.close()
}

Cancele la suscripción a su detector de cambios cuando ya no desee recibir notificaciones sobre las actualizaciones de los datos que supervisa. Para cancelar la suscripción a un detectorde cambios, cancele la corrutina que lo contiene.

// query for the specific object you intend to listen to
val fellowshipOfTheRing = realm.query(Fellowship::class, "name == 'Fellowship of the Ring'").first().find()!!
val members = fellowshipOfTheRing.members
// flow.collect() is blocking -- run it in a background context
val job = CoroutineScope(Dispatchers.Default).launch {
val membersFlow = members.asFlow()
membersFlow.collect { changes: ListChange<Character> ->
// change listener stuff in here
}
}
job.cancel() // cancel the coroutine containing the listener

Los cambios en documentos anidados a más de cuatro niveles de profundidad no activan notificaciones de cambio.

Si tiene una estructura de datos en la que necesita escuchar cambios cinco niveles más abajo o más profundos, las soluciones alternativas incluyen:

  • Refactorice el esquema para reducir la anidación.

  • Agregue algo como "push-to-refresh" para permitir que los usuarios actualicen los datos manualmente.

Volver

Agrupa un reino

En esta página