Realm(Java)數據庫使用文檔(通知Notifications)


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中的值非空,它將被空值覆蓋並觸發通知。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章