Menu Docs
Página inicial do Docs
/ /
Atlas Device SDKs
/ /

React a alterações - Kotlin SDK

Nesta página

  • Registrar um ouvinte de alteração de query
  • Registrar um ouvinte de alteração do RealmObject
  • Registrar um ouvinte de alteração de coleção
  • Registrar um ouvinte de alteração de caminho principal
  • Observar caminhos-chave aninhados
  • Observe os caminhos principais com curingas
  • Cancelar a assinatura de um ouvinte de alterações
  • Alterar limites de notificação

Qualquer aplicativo moderno deve ser capaz de React quando os dados mudam, independentemente da origem dessa alteração. Quando um usuário adiciona um novo item a uma lista, você pode querer atualizar a UI, mostrar uma notificação ou registrar uma mensagem. Quando alguém atualiza esse item, você pode querer alterar seu estado visual ou disparar uma solicitação de rede. Por fim, quando alguém exclui o item, você provavelmente quer removê-lo da UI. O sistema de notificação do Realm permite observar e React a alterações em seus dados, independentemente das gravações que causaram as alterações.

A arquitetura congelada do Kotlin SDK torna as notificações ainda mais importantes. Como o Kotlin SDK não tem objetos ativos que são atualizados automaticamente, você usará notificações para manter a interface do usuário e a camada de dados sincronizadas.

Você pode assinar as alterações nos seguintes eventos:

  • Query sobre a collection

  • Objeto de Realm

  • Realm collection (por exemplo, lista)

O SDK só fornece notificações para objetos aninhados em até quatro camadas de profundidade. Se você precisar React a alterações em objeto mais profundamente aninhados, registre um ouvinte de alteração de caminho principal. Para obter mais informações, consulte Registrar um ouvinte de alteração de caminho principal nesta página.

Você também pode React a alterações no estado de autenticação do usuário. Para obter mais informações, consulte Observar alterações de autenticação.

Exemplo

Sobre os exemplos nesta página

Os exemplos nesta página usam dois tipos de objeto de Realm, Character e 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
}
}

Os exemplos têm estes dados de amostra:

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()

Você pode registrar um manipulador de notificações em qualquer query dentro de um Realm. Primeiro, crie um fluxo Kotlin a partir da consulta com asFlow(). Em seguida, use o método collect() para lidar com eventos nesse fluxo. Eventos do tipo UpdatedResults registram todas as alterações nos objetos correspondentes à query usando as seguintes propriedades:

Propriedade
Tipo
Descrição
insertions
IntArray
Índices na nova collection adicionados nesta versão.
insertionRanges
Array<ListChangeSet.Range>
Faixas de índices na nova collection adicionadas nesta versão.
changes
IntArray
Índices dos objetos na nova coleção que foram modificados nesta versão.
changeRanges
Array<ListChangeSet.Range>
Faixas de índices na nova collection que foram modificadas nesta versão.
deletions
IntArray
Índices na versão anterior da collection que foram removidos desta.
deletionRanges
Array<ListChangeSet.Range>
Faixas de índices na versão anterior da collection que foram removidas desta.
list
RealmResults<T como RealmObject>
A collection de resultados está sendo monitorada quanto a alterações.
// 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
}
}

Você pode registrar um manipulador de notificações em um objeto específico dentro de um Realm. O Realm notifica seu manipulador quando qualquer propriedade do objeto é alterada. Para registrar um ouvinte de alterações em um único objeto, obtenha uma RealmSingleQuery com realm.query.first(). Gere um fluxo a partir dessa consulta com asFlow(). O manipulador recebe um objeto SingleQueryChange que comunica as alterações de objeto usando os seguintes subtipos:

Subtipo
Propriedades
Notas
UpdatedObject
changeFields, obj
Passe um nome de campo para isFieldChanged() para verificar se este campo foi alterado.
DeletedObject
obj
Como o obj sempre reflete a versão mais recente do objeto, ele sempre retorna um valor nulo nesse 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
}
}
}
}

Você pode registrar um manipulador de notificações em um RealmList, RealmSet ou RealmMap. O Realm notifica seu manipulador quando algum dos itens da collection é alterado. Primeiro, crie um Kotlin a partir da collection com asFlow(). Em seguida, use o método collect() para lidar com eventos nesse fluxo. evento do tipo ListChange, SetChange ou MapChange registram todas as alterações na collection.

RealmList As notificações implementam a interface ListChange , que descreve as possíveis alterações que podem ocorrer em uma coleção RealmList. Esses estados são representados por subclasses:

  • InitialList

  • UpdatedList

  • DeletedList

Um ListChangeSet é uma interface que modela as alterações que podem ocorrer em uma lista, e as propriedades incluem:

Propriedade
Tipo
Descrição
insertions
IntArray
Índices na nova collection adicionados nesta versão.
insertionRanges
Array<ListChangeSet.Range>
Faixas de índices na nova collection adicionadas nesta versão.
changes
IntArray
Índices dos objetos na nova coleção que foram modificados nesta versão.
changeRanges
Array<ListChangeSet.Range>
Faixas de índices na nova collection que foram modificadas nesta versão.
deletions
IntArray
Índices na versão anterior da collection que foram removidos desta.
deletionRanges
Array<ListChangeSet.Range>
Faixas de índices na versão anterior da collection que foram removidas desta.
list
RealmResults<T como RealmObject>
A collection de resultados está sendo monitorada quanto a alterações. Isso é fornecido quando o tipo de evento é UpdatedList.

RealmSet notificações implementam a interface SetChange , que descreve as possíveis alterações que podem ocorrer em uma coleção RealmSet. Esses estados são representados por subclasses:

  • InitialSet

  • UpdatedSet

  • DeletedSet

Um SetChangeSet é uma interface que modela as alterações que podem ocorrer em um conjunto, e as propriedades incluem:

Propriedade
Tipo
Descrição
deletions
Int
O número de entradas que foram excluídas nesta versão da coleção.
insertions
Int
O número de entradas que foram inseridas nesta versão da collection.
set
RealmSet<T>
O conjunto que está sendo monitorado quanto a alterações. Isso é fornecido quando o tipo de evento é um UpdatedSet.

RealmMap As notificações implementam a interface MapChange , que descreve as possíveis alterações que podem ocorrer em uma coleção RealmMap. Esses estados são representados por subclasses:

  • InitialMap

  • UpdatedMap

  • DeletedMap

Um MapChangeSet é uma interface que modela as alterações que podem ocorrer em um mapa, e as propriedades incluem:

Propriedade
Tipo
Descrição
deletions
Array<K>
As chaves que foram excluídas nesta versão do mapa.
insertions
Array<K>
As chaves que foram inseridas nesta versão do mapa.
changes
Array<K>
As chaves cujos valores foram alterados nesta versão da collection.
set
RealmMap<K, V>
O mapa sendo monitorado quanto a alterações. Isso é fornecido quando o tipo de evento é um 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
}
}
}
}

Novidade na versão 1.13.0.

Ao registrar um manipulador de notificações, você pode passar uma lista opcional de nomes de propriedades de string para especificar o caminho de chave ou caminhos de chave a serem observados.

Quando você especifica caminhos-chave, somente as alterações nesses caminhos-chave trigger bloqueios de notificação. Quaisquer outras alterações não bloqueia notificações de trigger.

No exemplo a seguir, registramos um ouvinte de alteração de caminho principal para a propriedade 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()
}

Observação

Vários tokens de notificação no mesmo objeto que filtram exclusivamente caminhos de chave separados não filtram. Se uma alteração de caminho-chavefor satisfeita para um token de notificação, todos os bloqueios de token de notificação para esse objeto serão executados.

Você pode usar a notação de pontos para observar caminhos de chaves aninhados. Por padrão, o SDK só relata notificações para objetos aninhados em até quatro camadas de profundidade. Observe um caminho de chave específico se você precisar observar alterações em objetos aninhados mais profundamente.

No exemplo a seguir, observamos as atualizações da propriedade aninhada members.age. Observe que o SDK relata a alteração na propriedade de nível superior members , embora estejamos atentos às alterações em uma propriedade aninhada:

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()
}

Você pode usar curingas (*) para observar alterações em todos os caminhos-chave no nível do curinga.

No exemplo a seguir, usamos a observação curinga para alterações em quaisquer propriedades aninhadas em um nível de profundidade na propriedade members . Com base no modelo usado neste exemplo, esse ouvinte de alterações relataria alterações nas propriedades age, species ou name de qualquer membro. Observe que o SDK relata a alteração na propriedade members de nível superior, embora estejamos atentos às alterações em uma propriedade aninhada:

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 a assinatura do seu ouvinte de alterações quando não quiser mais receber notificações sobre atualizações nos dados que ele está assistindo. Para cancelar a assinatura de um ouvinte de alterações, cancele o coroutine de encerramento.

// 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

Alterações em documentos aninhados com mais de quatro níveis abaixo não acionam notificações de alterações.

Se você tiver uma estrutura de dados em que precise escutar as alterações em cinco níveis para baixo ou mais profundamente, as soluções alternativas incluem:

  • Refatore o esquema para reduzir o aninhamento.

  • Adicione algo como "pressione para atualizar" para permitir que os usuários atualizem os dados manualmente.

Voltar

Agrupar um realm