Realm(Java)数据库使用文档(目录)
可以注册一个侦听器以接收有关Realm或其实体更改的通知。当Realm整体发生变化时,将发送领域通知;更改、添加或删除单个对象时,将发送收集通知。
通过调用removeChangeListener或removeAllChangeListeners方法来停止通知传递。如果注册侦听器的对象被垃圾回收,或者其Realm实例已关闭,则通知也将停止。只要您需要通知,就应该对正在听的对象保持强烈的参考。
// 错误通知注册方式。当该方法退出导致侦听器停止发出通知时,查询结果将进行GC处理。
public void runQuery() {
realm.where(Person.class)
.findAllAsync()
.addChangeListener(new RealmChangeListener() {
public void onChange(RealmResults<Person> persons) {
// Persons was updated
}
};
}
// 正确注册通知的方式。方法退出后,侦听器将继续发出通知。
RealmResults<Person> persons;
public void runQuery() {
persons = realm.where(Person.class)
.findAllAsync()
persons.addChangeListener(new RealmChangeListener() {
public void onChange(RealmResults<Person> persons) {
// Persons was updated
}
};
}
通知始终在最初注册的线程上传递。该线程必须具有正在运行的Looper。如果相关的写事务发生在不同的线程上,则在提交事务后将异步调用侦听器。
如果写事务在同一线程上发生,则在提交事务时将同步调用侦听器。但是,在某些情况下,事务开始时可能会调用侦听器-如果Realm已升级到最新版本,或者观察到的Realm实体已通过触发通知的方式进行了修改或删除。在这些情况下,侦听器在当前写入事务的上下文中运行,因此尝试在通知处理程序中开始新的写入事务将引发异常。您可以使用Realm.isInTransaction方法确定代码是否在写事务中执行。
由于异步通知是通过循环事件传递的,因此循环循环中的其他事件可能会延迟通知的传递。如果无法立即发送通知,则来自多个写入事务的更改可能会合并为一个通知。
10.1 Realm通知
您的UI或其他循环程序线程可以通过添加侦听器来获知Realm中的更改,该侦听器在更改Realm时执行:
public class MyActivity extends Activity {
private Realm realm;
private RealmChangeListener realmListener;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
realm = Realm.getDefaultInstance();
realmListener = new RealmChangeListener<Realm>() {
@Override
public void onChange(Realm realm) {
// ... do something with the updates (UI, etc.) ...
}
};
realm.addChangeListener(realmListener);
}
@Override
protected void onDestroy() {
super.onDestroy();
// Remove the listener.
realm.removeChangeListener(realmListener);
// Close the Realm instance.
realm.close();
}
}
Realm上的侦听器会接收整个更改的领域。
10.2 集合通知
集合通知不接收整个Realm,而是接收对更改的细粒度描述。这些包含自上次通知以来已添加,删除或修改的对象的索引。集合通知是异步传递的,首先是初始结果,然后是在每次更改集合中任何对象(或添加新对象)的写事务之后再次传递。
可以通过传递给更改侦听器的OrderedCollectionChangeSet
参数访问这些更改。该对象保存有关受删除,插入和更改影响的索引的信息。
前两个删除和插入记录已添加到集合中或从集合中删除的对象的索引。在将对象添加到Realm或Realm删除对象时会考虑到这一点。对于RealmResults,当您过滤特定值并且对象已更改以使其现在与查询匹配或不再匹配时,这也适用。
每当对象的字段发生更改时,您都会收到有关更改的通知,该字段以前是集合的一部分,现在仍然是集合的一部分。当一对多关系改变时,也会发生这种情况。
public class Dog extends RealmObject {
public String name;
public int age;
}
public class Person exteds RealmObject {
public String name;
public RealmList<Dog> dogs;
}
假设您正在观察上面model代码给出的狗主人名单。例如,在以下情况下,您将收到有关匹配的Person对象的修改的通知:
- 您修改此人的姓名。
- 您可以从属于某个人的狗列表中添加或删除狗。
- 您修改属于该人的狗的年龄。
这样就可以离散地控制对UI内部内容的动画和视觉更新,而不必在每次发生通知时随意重新加载所有内容。
private final OrderedRealmCollectionChangeListener<RealmResults<Person>> changeListener = new OrderedRealmCollectionChangeListener<RealmResults<Person>>() {
@Override
public void onChange(RealmResults<Person> collection, OrderedCollectionChangeSet changeSet) {
// null表示异步查询第一次返回。
if (changeSet == null) {
notifyDataSetChanged();
return;
}
// 对于删除,必须以相反的顺序通知适配器。
OrderedCollectionChangeSet.Range[] deletions = changeSet.getDeletionRanges();
for (int i = deletions.length - 1; i >= 0; i--) {
OrderedCollectionChangeSet.Range range = deletions[i];
notifyItemRangeRemoved(range.startIndex, range.length);
}
OrderedCollectionChangeSet.Range[] insertions = changeSet.getInsertionRanges();
for (OrderedCollectionChangeSet.Range range : insertions) {
notifyItemRangeInserted(range.startIndex, range.length);
}
OrderedCollectionChangeSet.Range[] modifications = changeSet.getChangeRanges();
for (OrderedCollectionChangeSet.Range range : modifications) {
notifyItemRangeChanged(range.startIndex, range.length);
}
}
};
RealmRecyclerViewAdapter提供了开箱即用的功能。
10.3 对象通知
Realm支持对象级通知。您可以在特定的RealmObject
上注册一个通知,以便在删除对象时或在对象上的任何托管字段修改其值时得到通知。
只有托管的RealmObjects
可以在其上注册侦听器。
可以通过传递给更改侦听器的ObjectChangeSet
参数来访问这些更改。ObjectChangeSet
包含有关更改了哪些字段以及是否删除了RealmObject
的信息。
如果删除了对象,则ObjectChangeSet.isDeleted
将返回true
。之后,将不会再呼叫该收听者。
如果对象的任何托管字段均发生更改,则ObjectChangeSet.getChangedFields
将返回更改的字段的名称。您还可以使用ObjectChangeSet.isFieldChanged
来测试给定字段是否刚刚更改。
private final RealmObjectChangeListener<Dog> listener = new RealmObjectChangeListener<Dog>() {
@Override
public void onChange(Dog dog, ObjectChangeSet changeSet) {
if (changeSet.isDeleted()) {
Log.i(TAG, "The dog was deleted");
return;
}
for (String fieldName : changeSet.getChangedFields()) {
Log.i(TAG, "Field " + fieldName + " was changed.");
}
}
};
10.4 相同值的通知
Realm将所有更改视为会发出通知的内容。但是,在很多情况下,如果值没有更改,则不希望刷新UI。
如果要更新单个字段,则可以在覆盖它之前检查Realm文件中的值,以避免触发通知:
realm.executeTransaction(new Realm.Transaction() {
@Override
public void execute(Realm realm) {
String newName = "Jane";
Person managedPerson = getPerson();
if (!newName.equals(managedPerson.getName())) {
managedPerson.setName(newName);
}
}
});
如果使用Realm.copyToRealm()
或Realm.copyToRealmOrUpdate()
插入对象,则可以使用ImportFlag
指示仅应实际更改的字段进行更新:
realm.executeTransaction(new Realm.Transaction() {
@Override
public void execute(Realm realm) {
int id = 42;
Person person = new Person(id, "Jane");
realm.copyToRealmOrUpdate(person, ImportFlag.CHECK_SAME_VALUES_BEFORE_SET);
}
});
这样,仅针对实际更改的字段发出通知。使用此标志时,输入对象中的空值将像其他值一样被视为普通值,因此,如果Realm中的值非空,它将被空值覆盖并触发通知。