React - Java SDK
Realm客户端中的对象是活动对象,会自动更新以反映数据更改(包括同步的远程更改),并在根本的数据发生更改时发出您可以订阅的通知事件。
任何现代应用程序都应该能够在数据发生变化时React,无论更改源自何处。当用户向列表添加新项目时,您可能希望更新用户界面、显示通知或记录消息。 当有人更新该项目时,您可能希望更改其视觉状态或发出网络请求。 最后,当有人删除该项目时,您可能希望将其从用户界面中删除。 Realm 的通知系统允许您监视数据更改并做出 React,而与导致更改的写入无关。
Realm 会发出三种通知:
每当特定 Realm 提交写事务时,Realm 都会收到 Realm 通知。
每当集合中的任何Realm 对象发生更改(包括插入、更新和删除)时,都会收到集合通知。
每当特定 Realm 对象发生更改(包括更新和删除)时都会收到对象通知。
自动刷新
在与 Looper 关联的线程上访问的Realm对象 定期自动更新以反映根本的数据的更改。
Android 用户界面线程始终包含一个 Looper
实例。 如果您需要在任何其他线程上长时间保留 Realm 对象,则应为该线程配置Looper
。
警告
始终关闭非 Looper 线程上的 Realm 实例以避免资源泄漏
没有 Looper 的线程上的 Realm 不会自动推进其版本。这会增加内存和磁盘中域的大小。 尽可能避免在非 Looper 线程上使用域实例。 如果确实在非 Looper 线程上打开了域 ,请在使用域将其关闭。
注册 Realm 变更监听器
您可以在整个 Realm 上注册通知处理程序。 每当提交涉及该域的任何写事务(write transaction)时,Realm 都会调用通知处理程序。处理程序不会收到有关变更的信息。
当您想知道是否发生了更改但又不想知道具体更改的内容时,此功能非常有用。 例如,概念验证应用通常使用这种通知类型,并在发生任何变化时刷新整个用户界面。 随着应用变得更加复杂和对性能敏感,应用开发者转向更精细的通知。
例子
假设您正在编写一个实时协作应用程序。 为了给人一种应用程序充满协作活动的感觉,您希望有一个指示器,在进行任何更改时都能亮起。 在这种情况下,Realm 通知处理程序将是驱动控制指标的代码的好方法。 以下代码展示了如何使用addChangeListener() 观察 Realm 的更改:
public class MyActivity extends Activity { private Realm realm; private RealmChangeListener realmListener; protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); realm = Realm.getDefaultInstance(); realmListener = new RealmChangeListener<Realm>() { public void onChange(Realm realm) { // ... do something with the updates (UI, etc.) ... } }; // Observe realm notifications. realm.addChangeListener(realmListener); } protected void onDestroy() { super.onDestroy(); // Remove the listener. realm.removeChangeListener(realmListener); // Close the Realm instance. realm.close(); } }
class MyActivity : Activity() { private lateinit var realm: Realm private lateinit var realmListener: RealmChangeListener<Realm> override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) realm = Realm.getDefaultInstance() realmListener = RealmChangeListener { // ... do something with the updates (UI, etc.) ... } // Observe realm notifications. realm.addChangeListener(realmListener) } override fun onDestroy() { super.onDestroy() // Remove the listener. realm.removeChangeListener(realmListener) // Close the Realm instance. realm.close() } }
重要
自动刷新
当新的更改写入 Realm 时,所有包含Looper
的线程都会自动刷新RealmObject
和RealmResult
实例。 因此,在ReactRealmChangeListener
时无需再次获取这些对象,因为这些对象已更新并准备好重新绘制到屏幕上。
注册集合变更监听器
您可以在特定的collection中的域上注册通知处理程序。处理程序接收自上次通知以来的变更描述。 具体来说,此描述由三个索引列表组成:
已删除对象的索引。
已插入对象的索引。
已修改对象的索引。
通过调用removeChangeListener()
或removeAllChangeListeners()
方法停止通知传送。 如果出现以下情况,通知也会停止:
注册侦听器的对象将被垃圾收集。
Realm 实例关闭。
只要需要通知,就保持对正在侦听的对象的强引用。
重要
顺序很重要
在集合通知处理程序中,始终按以下顺序应用变更:删除、插入和修改。在删除操作之前处理插入操作可能会导致意外行为。
Realm 在检索collection后会发出初始通知。此后,每当写事务添加、更改或删除集合中的对象时,Realm 都会异步传递集合通知。
与 域 通知不同,collection 通知包含有关变更的详细信息。这样就可以对变更做出复杂且有选择性的反应。 collection通知提供托管用户界面中表示collection的列表或其他视图所需的所有信息。
以下代码展示了如何使用addChangeListener()观察集合的更改:
RealmResults<Dog> dogs = realm.where(Dog.class).findAll(); // Set up the collection notification handler. OrderedRealmCollectionChangeListener<RealmResults<Dog>> changeListener = (collection, changeSet) -> { // For deletions, notify the UI in reverse order if removing elements the UI OrderedCollectionChangeSet.Range[] deletions = changeSet.getDeletionRanges(); for (int i = deletions.length - 1; i >= 0; i--) { OrderedCollectionChangeSet.Range range = deletions[i]; Log.v("EXAMPLE", range.length + " dogs deleted at " + range.startIndex); } OrderedCollectionChangeSet.Range[] insertions = changeSet.getInsertionRanges(); for (OrderedCollectionChangeSet.Range range : insertions) { Log.v("EXAMPLE", range.length + " dogs inserted at " + range.startIndex); } OrderedCollectionChangeSet.Range[] modifications = changeSet.getChangeRanges(); for (OrderedCollectionChangeSet.Range range : modifications) { Log.v("EXAMPLE", range.length + " dogs modified at " + range.startIndex); } }; // Observe collection notifications. dogs.addChangeListener(changeListener);
val dogs = realm.where(Dog::class.java).findAll() // Set up the collection notification handler. val changeListener = OrderedRealmCollectionChangeListener { collection: RealmResults<Dog>?, changeSet: OrderedCollectionChangeSet -> // For deletions, notify the UI in reverse order if removing elements the UI val deletions = changeSet.deletionRanges for (i in deletions.indices.reversed()) { val range = deletions[i] Log.v("EXAMPLE", "${range.length} dogs deleted at ${range.startIndex}") } val insertions = changeSet.insertionRanges for (range in insertions) { Log.v("EXAMPLE", "${range.length} dogs inserted at ${range.startIndex}") } val modifications = changeSet.changeRanges for (range in modifications) { Log.v("EXAMPLE", "${range.length} dogs modified at ${range.startIndex}") } } // Observe collection notifications. dogs.addChangeListener(changeListener)
注册对象变更侦听器
您可以在 Realm 中的特定对象上注册通知处理程序。Realm 会通知您的处理程序:
当删除对象时。
当对象的任何属性发生变更时。
处理程序接收有关哪些字段已变更以及对象是否已被删除的信息。
通过调用removeChangeListener()
或removeAllChangeListeners()
方法停止通知传送。 如果出现以下情况,通知也会停止:
注册侦听器的对象将被垃圾收集。
Realm 实例关闭。
只要需要通知,就保持对正在侦听的对象的强引用。
以下代码展示了如何在域中创建类的新实例,并使用addChangeListener()观察该实例的变更:
// Create a dog in the realm. AtomicReference<Dog> dog = new AtomicReference<Dog>(); realm.executeTransaction(transactionRealm -> { dog.set(transactionRealm.createObject(Dog.class, new ObjectId())); dog.get().setName("Max"); }); // Set up the listener. RealmObjectChangeListener<Dog> listener = (changedDog, changeSet) -> { if (changeSet.isDeleted()) { Log.i("EXAMPLE", "The dog was deleted"); return; } for (String fieldName : changeSet.getChangedFields()) { Log.i("EXAMPLE", "Field '" + fieldName + "' changed."); } }; // Observe object notifications. dog.get().addChangeListener(listener); // Update the dog to see the effect. realm.executeTransaction(r -> { dog.get().setName("Wolfie"); // -> "Field 'name' was changed." });
// Create a dog in the realm. var dog = Dog() realm.executeTransaction { transactionRealm -> dog = transactionRealm.createObject(Dog::class.java, ObjectId()) dog.name = "Max" } // Set up the listener. val listener = RealmObjectChangeListener { changedDog: Dog?, changeSet: ObjectChangeSet? -> if (changeSet!!.isDeleted) { Log.i("EXAMPLE", "The dog was deleted") } else { for (fieldName in changeSet.changedFields) { Log.i( "EXAMPLE", "Field '$fieldName' changed." ) } } } // Observe object notifications. dog.addChangeListener(listener) // Update the dog to see the effect. realm.executeTransaction { r: Realm? -> dog.name = "Wolfie" // -> "Field 'name' was changed." }
取消注册变更监听器
您可以通过将变更监听器传递给Realm.removeChangeListener()来取消注册变更监听器。 您可以使用Realm.removeAllChangeListeners() 取消注册当前订阅 Realm 或其任何链接对象或集合中变更的所有变更侦听器。
在自定义 ROM 上的系统应用中使用 Realm
Realm 使用管来支持通知以及从多个进程访问 Realm 文件。虽然普通用户应用程序默认允许这样做,但系统应用程序不允许这样做。
您可以通过在 Android 清单中设置android:sharedUserId="android.uid.system"
来定义系统应用。 使用系统应用时,您可能会在 Logcat 中看到如下所示的安全违规:
05-24 14:08:08.984 6921 6921 W .realmsystemapp: type=1400 audit(0.0:99): avc: denied { write } for name="realm.testapp.com.realmsystemapp-Bfqpnjj4mUvxWtfMcOXBCA==" dev="vdc" ino=14660 scontext=u:r:system_app:s0 tcontext=u:object_r:apk_data_file:s0 tclass=dir permissive=0 05-24 14:08:08.984 6921 6921 W .realmsystemapp: type=1400 audit(0.0:100): avc: denied { write } for name="realm.testapp.com.realmsystemapp-Bfqpnjj4mUvxWtfMcOXBCA==" dev="vdc" ino=14660 scontext=u:r:system_app:s0 tcontext=u:object_r:apk_data_file:s0 tclass=dir permissive=0
要解决此问题,您需要调整 ROM 中的 SELinux 安全规则。 这可以通过使用工具audit2allow
来完成,该工具作为 AOSP 的一部分提供:
从设备中拉取当前策略:
adb pull /sys/fs/selinux/policy 将 SELinux 错误复制到名为 input.txt 的文件中。
运行
audit2allow
工具:audit2allow -p policy -i input.txt 该工具应输出一条规则,您可以将其添加到现有策略中,以支持使用 Realm。
下面提供了此类策略的示例:
# Allow system_app to create named pipes required by Realm # Credit: https://github.com/mikalackis/platform_vendor_ariel/blob/master_oreo/sepolicy/system_app.te allow system_app fuse:fifo_file create; allow system_app system_app_data_file:fifo_file create; allow system_app system_app_data_file:fifo_file { read write }; allow system_app system_app_data_file:fifo_file open;
变更通知限制
嵌套文档中深度超过四级的变更不会触发变更通知。
如果您的数据结构需要侦听第五层深度或更深层的更改,解决方法包括:
重构模式以减少嵌套。
添加“推送以刷新”一类的内容,使用户能够手动刷新数据。