2016-11-18 66 views
3

場景

我有一個典型的UI線程和工作線程場景。我做了一些工作,並在工作線程中將結果寫入領域。結果是一個帶有一些String字段的簡單RealmObject。完成此操作後,我將UI線程上的事件發送到我的活動(使用Otto事件總線)以報告工作已完成。Realm在多線程場景中給出陳舊結果

在我的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); 

問題

  1. 什麼是起牀最新RealmObject正確的方法是什麼?我是否每次都必須將它們放入事務塊中以強制它與工作線程「同步」?有我可以遵循的模式嗎?

  2. 開始領域事務(通過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

嘗試使用setSomething()方法。我相信領域代理對象使用它。不確定,但值得嘗試 –

+0

@Tim我在我的實際代碼中使用了setSomething()方法。結果仍然是一樣的。給出的例子僅用於說明目的。 – Weizhi

+0

建議:發佈你的實際代碼 –

回答

1

領域使用特殊的 「聽衆」 線程將消息到UI線程隊列的Looper與其他線程溝通。我們不提供任何擔保,因爲這可能會因爲多種原因而推遲。 Otto會直接發送消息,這很可能會在循環消息到達之前發生。在這種情況下,UI線程中的數據將顯示爲「陳舊」。

對這些類型的通知使用Realm更改偵聽器會好得多。在這種情況下,您會在數據準備就緒時收到通知。

參見:https://github.com/realm/realm-java/issues/3427

+0

嗨基督教謝謝你的答案。第二個問題呢?事務塊是否會從另一個線程讀取/寫入? – Weizhi

+0

寫入交易在所有線程間相互阻塞。讀取事務永遠不會被阻塞(即使寫入事務正在進行,您也可以始終訪問您的數據)。 –

2

UI線程的境界是由後臺守護線程,而不是因爲2.0.0

即使在2.0.0然而,異步查詢的直接消息鉤子的消息隊列更新延遲結果的同步版本更新,直到評估所有異步查詢。

所以奧托直接發送消息,並立即期待UI線程是最新的只有在使用同步查詢(.findAll()findAllSorted()等),並使用領域1.2.0或領域的作品1.1.1或更早版本。


規範的解決方案是使用RealmChangeListener來代替變更通知。

非官方的解決辦法是迫使王國到直接刷新本身和它的所有結果,但是這是混亂和力量異步查詢同步執行,所以它不是真正的建議。


至於交易,它阻止所有其他線程執行交易,直到當前交易完成。在交易中,您總是會看到最新版本的Realm。