android-Realm在多线程方案中给出过时的结果

情境

我有一个典型的UI线程和辅助线程方案.我做了一些工作,并将结果写到工作线程中的领域.结果是带有一些String字段的简单RealmObject.完成此操作后,我将UI线程上的事件发送到我的Activity(使用Otto事件总线)以报告工作已完成.

在我的活动中接收到事件后,我将查询结果,并且String字段未使用写入值进行更新.

在工作线程上:

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

解决方法:

领域使用特殊的“侦听器”线程与其他线程通信,该线程将消息放入UI线程Looper队列中.我们不会提供任何保证,因为多种原因可能会延迟.奥托(Otto)将直接发送一条消息,这很可能会在回圈消息到达之前发生.在这种情况下,UI线程上的数据将显示为“陈旧”.

对于这些类型的通知,最好使用Realm更改侦听器.在这种情况下,数据准备就绪时会通知您.

另请参阅:https://github.com/realm/realm-java/issues/3427

上一篇:android-使用kotlin设置领域时出错


下一篇:shiro认证和授权