我有一个典型的UI线程和工作线程场景。我做了一些工作,并将结果写入工作线程中的领域。结果是一个带有一些String字段的简单RealmObject。完成后,我将UI线程上的事件发送到我的Activity(使用Otto事件总线)来报告工作已完成。
在我的Activity中接收到事件后,我查询结果,并且字符串字段未使用写入值更新。
在工作线程上:
// Did some work. Got some result
// Write to realm
try {
realm = Realm.getDefaultInstance();
realm.executeTransaction(new Realm.Transaction() {
@Override
public void execute(Realm realm) {
MyResult result = realm.where(MyResult.class)
.equalTo("id", 1)
.findFirst();
result.someString = "hello world";
}
});
} finally {
if(realm != null){
realm.close();
realm = null;
}
}
//Post job done event on Otto bus
uiThreadBus.post(new JobDoneEvent());
活动:
// Upon received JobDoneEvent
MyResult result = realm.where(MyResult.class)
.equalTo("id", 1)
.findFirst();
// result.someString is some stale value
Log.d("TAG", result.someString);
我意识到如果我将查询包装在事务块中,那么当我尝试打印它时,RealmObject将是最新的。
// Upon received JobDoneEvent
MyResult result = null;
try{
realm.beginTransaction();
result = realm.where(MyResult.class)
.equalTo("id", 1)
.findFirst();
realm.cancelTransaction();
}
catch(Exception e) {
realm.cancelTransaction();
}
// result.someString is up-to-date
Log.d("TAG", result.someString);
获取最新RealmObject的正确方法是什么?我是否必须每次都将它们放入事务块中以强制它与工作线程“同步”?是否有我可以遵循的模式?
开始一个领域事务(通过Realm#beginTransaction()或Realm#executeTransaction())究竟做了什么?它阻止其他线程的读/写尝试吗?在交易中执行长期操作(例如网络请求)是否有任何损害?
实际代码:
// Did some work. Got some result
// Write to realm
try {
realm = Realm.getDefaultInstance();
realm.executeTransaction(new Realm.Transaction() {
@Override
public void execute(Realm realm) {
User managedUser = result.payload.createOrUpdateInRealm(realm,
MyApplication.getPrimaryKeyFactory());
Log.i("TAG", "updated user: " + managedUser.getId());
}
});
} finally {
if(realm != null) {
realm.close();
realm = null;
}
}
//Post job done event on Otto bus
MyApplication.getBusInstance().post(new LoginEvent());
// Writing to realm method
public User createOrUpdateInRealm(@NonNull Realm realm,
@NonNull PrimaryKeyFactory pkFactory) {
User managedUser = realm.where(User.class)
.equalTo("primary_key", pk)
.findFirst();
managedUser.setId(xUserId);
return managedUser;
}
// Event receiving method in Activity
@Subscribe
public void loginEventReceived(LoginEvent event) {
User user = mRealm.where(User.class)
.equalTo("primary_key", mPk)
.findFirst();
Log.d("TAG", user.getId());
}
答案 0 :(得分:2)
UI线程的Realm由后台守护程序线程更新,而不是自2.0.0以来直接发送到Looper消息队列的消息
即使在2.0.0之前,异步查询也会延迟结果的同步版本更新,直到评估完所有异步查询。
所以Otto直接发送消息,并且只有在您使用仅同步查询(.findAll()
,findAllSorted()
等时,才会立即期望UI线程保持最新状态。 。),并使用Realm 1.2.0或Realm 1.1.1或更早版本。
规范的解决方案是使用RealmChangeListener
来通知变更。
非官方的解决方案是强制Realm直接刷新自身及其所有结果,但这很麻烦并迫使异步查询同步执行,所以并不是真的推荐。
对于事务,它阻止所有其他线程执行事务,直到完成当前事务。在事务中,您始终可以看到最新版本的Realm。
答案 1 :(得分:1)
Realm使用特殊的“侦听器”线程与其他线程进行通信,该线程将消息放入UI线程Looper队列中。我们不会在发生这种情况时提供任何保证,因为它可能由于多种原因而延迟。 Otto将直接发送消息,这很可能会在looper消息到达之前发生。在这种情况下,UI线程上的数据将显示为“陈旧”。
对这些类型的通知使用Realm更改侦听器要好得多。在这种情况下,您将在数据准备就绪时收到通知。