Docs 菜单

Docs 主页开发应用程序Atlas Device SDKs

React - Kotlin SDK

在此页面上

  • 注册查询变更监听器
  • 注册 RealmObject 变更监听器
  • 注册集合变更监听器
  • 注册键路径变更侦听器
  • 观察嵌套键路径
  • 使用通配符观察键路径
  • 取消订阅变更侦听器
  • 变更通知限制

任何现代应用程序都应该能够在数据发生变化时React,无论更改源自何处。当用户向列表添加新项目时,您可能希望更新用户界面、显示通知或记录消息。 当有人更新该项目时,您可能希望更改其视觉状态或发出网络请求。 最后,当有人删除该项目时,您可能希望将其从用户界面中删除。 Realm 的通知系统允许您监视数据更改并做出 React,而与导致更改的写入无关。

Kotlin SDK 的冻结架构使通知变得更加重要。 由于 Kotlin SDK 没有自动更新的活动对象,因此您将使用通知来保持用户界面和数据层同步。

您可以订阅以下事件的变更:

SDK 仅为嵌套深度不超过四层的对象提供通知。 如果需要对嵌套更深的React中的更改做出对象,请注册键路径更改侦听器。有关更多信息,请参阅本页上的注册键路径更改侦听器

您还可以对用户身份验证状态的变化做出反应。 有关更多信息,请参阅观察身份验证更改。

例子

关于本页中的示例

本页中的示例使用了两种 Realm 对象类型:CharacterFellowship

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

这些示例包含以下示例数据:

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

您可以为 Realm 中的任何查询注册通知处理程序。首先,使用asFlow()从查询中创建 Kotlin 流。接下来,使用collect()方法处理该流上的事件。 UpdatedResults类型的事件使用以下属性记录与查询匹配的对象的所有更改:

属性
类型
说明
insertions
IntArray
此版本中添加的新集合的索引。
insertionRanges
Array<ListChangeSet.Range>
此版本中添加的新collection中的索引范围。
changes
IntArray
此版本中修改的新集合中对象的索引。
changeRanges
Array<ListChangeSet.Range>
在此版本中修改的新集合中的索引范围。
deletions
IntArray
先前版本的集合索引已从本版本中删除。
deletionRanges
Array<ListChangeSet.Range>
已从本版本中删除的上一个版本的collection中的索引范围。
list
RealmResults<T 作为 RealmObject>
正受到变更监控的结果集合。
// 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
}
}

您可以在 Realm 中的特定对象上注册通知处理程序。当对象的任何属性发生变化时,Realm 会通知您的处理程序。要在单个对象上注册变更侦听器,请使用realm.query.first()获取RealmSingleQuery 。使用asFlow()从该查询生成流。处理程序接收一个SingleQueryChange对象,该对象使用以下子类型传达对象更改:

子类型
属性
注意
UpdatedObject
ChangedFields , obj
将字段名称传递给isFieldChanged()以检查该字段是否已更改。
DeletedObject
obj
由于obj始终反映对象的最新版本,因此它始终在此子类型中返回 null 值。
// 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
}
}
}
}

您可以在RealmListRealmSetRealmMap上注册通知处理程序。 当任何collection项发生变化时,Realm 会通知您的处理程序。首先,使用asFlow()从collection中创建 Kotlin 流程。接下来,使用collect()方法处理该流上的事件。 类型为ListChangeSetChangeMapChange的Events记录对collection的所有更改。

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

版本 1.13.0 中的新增内容

注册通知处理程序时,可以传递可选的字符串属性名称列表,以指定要监视的一个或多个键路径。

当您指定键路径时,只有这些键路径的更改才会trigger通知块。任何其他更改都不会trigger通知块。

在以下示例中,我们为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()
}

注意

同一对象上的多个通知令牌(针对单独的键路径进行过滤)不会进行排他性过滤。如果一项键路径变更符合一个通知令牌的条件,则该对象的所有通知令牌块都将执行。

您可以使用点表示法来观察嵌套的键路径。 默认情况下,SDK 仅报告嵌套深度不超过四层的对象的通知。 如果需要观察嵌套更深的对象的更改,请观察特定的键路径。

在以下示例中,我们将监视嵌套属性members.age的更新。 请注意,即使我们正在监视嵌套属性的更改,SDK 也会报告顶级members属性的更改:

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

您可以使用通配符 ( * ) 来观察通配符级别所有键路径的更改。

在以下示例中,我们使用通配符监视对members属性内一层深度的任何嵌套属性的更改。 根据此示例中使用的模型,此更改侦听器将报告对任何成员的agespeciesname属性的更改。 请注意,即使我们正在监视嵌套属性的更改,SDK 也会报告顶级members属性的更改:

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

当您不想再接收变更监听器所监控数据的更新通知时,请取消订阅变更监听器。要取消订阅变更侦听器, 请取消封闭的协程。

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

嵌套文档中深度超过四级的变更不会触发变更通知。

如果您的数据结构需要侦听第五层深度或更深层的更改,解决方法包括:

  • 重构模式以减少嵌套。

  • 添加“推送以刷新”一类的内容,使用户能够手动刷新数据。

← 捆绑 Realm - Kotlin SDK