Realm(Java)數據庫使用文檔(Threading)


Realm(Java)數據庫使用文檔(目錄)

Realm使得在多個線程上處理數據變得毫不費力,而不必擔心一致性或性能,因爲 對象 objects查詢 queries 始終在自動更新。您可以對不同線程中的活動對象進行操作,對其進行讀寫操作,而無需擔心其他線程對這些相同對象所做的事情。 如果需要更改數據,則可以使用事務。另一個線程中的其他對象將近乎實時地更新(更新將作爲事件在Looper上進行調度,因此Looper線程將在事件處理後立即進行更新)。

唯一的限制是您不能在線程之間隨機傳遞Realm對象。如果在另一個線程上需要相同的數據,則需要在另一個線程上查詢該數據。此外,您可以使用Realms反應式體系結構觀察更改。請記住,所有對象在線程之間都是最新的-數據更改時,Realm會通知您。

13.1 線程示例

假設我們有一個顯示客戶列表的應用程序。在後臺線程(Android IntentService)中,我們輪詢新客戶的遠程端點,然後將其保存到Realm。 當後臺線程添加新客戶時,UI線程中的數據將自動更新。UI線程通過RealmChangeListener通知,此時我們告訴UI小部件進行自我更新。無需重新查詢,因爲Realm可以使所有內容保持最新

// in a Fragment or Activity, etc

// 只要不對查詢結果進行垃圾回收,就只會觸發偵聽器,因此請保留對其的強大類引用。
private RealmResults<Customer> customers;

@Override
public void onActivityCreated(Bundle savedInstanceState) {
    // ... 爲簡潔起見,省略了樣板
    realm = Realm.getDefaultInstance();
    // get all the customers
    customers = realm.where(Customer.class).findAllAsync();
    // ... build a list adapter and set it to the ListView/RecyclerView/etc

    // set up a Realm change listener
    changeListener = new RealmChangeListener() {
        @Override
        public void onChange(RealmResults<Customer> results) {
            // 每當Realm數據庫在任何線程上更改時,都會調用此方法。
            // 請注意,更改偵聽器僅在Looper線程上起作用。
            // 對於非循環線程,您必須手動使用Realm.waitForChange()。
            updateUi();
        }
    };
    // 當客戶結果發生變化(添加、刪除、更新的項目等)時,告訴Realm通知我們的監聽器。
    customers.addChangeListener(changeListener);
}

// In a background service, in another thread
public class PollingService extends IntentService {
    @Override
    public void onHandleIntent(Intent intent) {
        Realm realm = Realm.getDefaultInstance();
        try {
            // 去做一些網絡電話/等,並獲取一些數據並將其填充到“ json”變量中
            String json = customerApi.getCustomers();
            realm.beginTransaction();
            realm.createObjectFromJson(Customer.class, json); // Save a bunch of new Customer objects
            realm.commitTransaction();
            // 此時,UI線程中的數據已經是最新的。
            // ...
        } finally {
            realm.close();
        }
    }
    // ...
}

後臺服務將新Customer添加到realm後,Customer列表將在UI中自動更新,而無需您進行任何其他干預。單個對象也是如此。假設您只管理一個對象。只需在一個線程上進行更改,UI線程就會自動擁有新數據。如果您需要對這一更改做出迴應,只需像上面一樣添加一個偵聽器即可。

13.2 跨線程使用Realm

跨線程使用Realm的唯一規則是記住Realm,RealmObject和RealmResults實例不能跨線程傳遞。而是使用異步查詢異步事務將操作轉移到後臺線程,並將所有結果返回給您。

當您想從其他線程訪問相同數據時,可以獲取一個新的Realm實例(即Realm.getInstance(RealmConfiguration config)或其重構方法),並通過查詢獲取對象。

這些對象將映射到磁盤上的相同數據,並且可以從任何線程讀取和寫入。

13.3 Android框架線程

使用這些類時要小心:

  • AsyncTask
  • IntentService

AsyncTask類包含執行後臺線程的doInBackground()方法。 IntentService類包含在工作線程中執行的onHandleIntent(Intent intent)方法。

如果您需要在這兩種方法中使用Realm,則應打開Realm,執行工作,然後在退出之前關閉Realm。 以下是幾個示例。

13.3.1 異步任務

如下所示,在doInBackground方法中打開和關閉Realm。

private class DownloadOrders extends AsyncTask<Void, Void, Long> {
    protected Long doInBackground(Void... voids) {
        // Now in a background thread.

        // Open the Realm
        Realm realm = Realm.getDefaultInstance();
        try {
            // Work with Realm
            realm.createAllFromJson(Order.class, api.getNewOrders());
            Order firstOrder = realm.where(Order.class).findFirst();
            long orderId = firstOrder.getId(); // Id of order
            return orderId;
        } finally {
            realm.close();
        }
    }

    protected void onPostExecute(Long orderId) {
        // 回到主線程,使用orderId進行操作,例如查詢Realm以獲取訂單並執行一些操作。
    }
}

13.3.2 IntentService

ChangeListeners不能在IntentService中工作。 即使是Looper線程,每次onHandleIntent的調用都是一個單獨的事件,不會“循環”。這意味着可以註冊更改偵聽器,但是它們永遠不會被觸發。

如下所示,通過onHandleIntent方法打開和關閉Realm。

public class OrdersIntentService extends IntentService {
    public OrdersIntentService(String name) {
        super("OrdersIntentService");
    }

    @Override
    protected void onHandleIntent(Intent intent) {
        // Now in a background thread.

        // Open the Realm
        Realm realm = Realm.getDefaultInstance();
        try {
            // Work with Realm
            realm.createAllFromJson(Order.class, api.getNewOrders());
            Order firstOrder = realm.where(Order.class).findFirst();
            long orderId = firstOrder.getId(); // Id of order
        } finally {
            realm.close();
        }
    }
}

13.4 多進程支持

可以從多個進程訪問realm,但有一些限制。當從同一APK中的不同進程訪問同一Realm時,包括通知在內的所有內容均應正常工作。

13.5 凍結對象

該功能是Realm 7.0版以來的新增功能,目前(翻譯時間爲2020.3.29)處於測試階段。您正在查看Realm beta版本7.0.0-beta的文檔。要查看最新的穩定版文檔,請參閱Realm 6.0.2

儘管在許多情況下實時更新線程受限的Realm Objects效果很好,但在某些情況下,例如, 當Realm是基於事件流的體系結構的一部分時,在該對象中,可以從多個線程讀取對象,然後最終以處理後的形式將其發送到UI線程。在這些情況下,凍結Realm、RealmResults、RealmList或Realm對象可能是有益的。

凍結Realm會返回一個不變的版本,可以從任何線程讀取和查詢該版本。來自凍結Realm的所有派生對象也將凍結。

Realm liveRealm = Realm.getDefaultInstance();
Realm frozenRealm = liveRealm.freeze();

new Thread(() -> {
  RealmResults<Person> frozenPersons = frozenRealm.where(Person.class).findAll();
  frozenPersons.isFrozen(); // true
  Person frozenPerson = frozenPersons.first();
  frozenPerson.isFrozen(); // true
}).start();

最常見的模式是將更改偵聽器附加到實時查詢結果,然後凍結結果,然後再傳遞給進一步處理:

Realm liveRealm = Realm.getDefaultInstance();
RealmResults<Person> liveResults = liveRealm.where(Person.class).findAllAsync();
liveResults.addChangeListener(result -> {
  doExpensiveWorkOnOtherThread(result.freeze());
});

無法在凍結的Realm上開始寫事務,修改凍結的對象或向其添加更改偵聽器。所有這些方法都將引發IllegalStateException。要修改凍結的對象,請在實時Realm中對其進行查詢,然後對其進行修改。

Realm liveRealm = Realm.getDefaultInstance();
Person frozenPerson = liveRealm.where(Person.class).findFirst().freeze();
frozenPerson.addChangeListener(listener); // Throws

new Thread(() -> {
  Realm bgLiveRealm = Realm.getDefaultInstance();
  bgLiveRealm.beginTransaction();
  frozenPerson.setName("Jane"); // Throws
  Person bgLivePerson = bgLiveRealm.where(Person.class).equalTo("id", frozenPerson.getId()).findFirst();
  bgLivePerson.setName("Jane");
  bgLiveRealm.commitTransaction();
  bgLiveRealm.close();
}).start();

一旦凍結,就無法解凍對象。使用isFrozen()查詢對象的狀態。即使對於其他線程限制的Realm對象,此方法也是線程安全的。

Realm liveRealm = Realm.getDefaultInstance();
Realm frozenRealm = liveRealm.freeze();

liveRealm.isFrozen(); // false;
frozenRealm.isFrozen(); // true

new Thread(() -> {
  liveRealm.isFrozen(); // false;
  frozenRealm.isFrozen(); // true
}).start();

只要產生凍結對象的活動Realm保持打開狀態,凍結對象就可用。在使用凍結的對象完成所有線程之前,必須注意不要關閉實時Realm。在關閉實時Realm之前,可以關閉凍結的Realm。

Realm liveRealm = Realm.getDefaultInstance();
Realm frozenRealm = liveRealm.freeze();
Person frozenPerson = frozenRealm.where(Person.class).findFirst();
frozenPerson.isValid(); // true

// 關閉實時Realm,也關閉凍結的數據
liveRealm.close();
frozenPerson.isValid(); // false

請注意,緩存過多的凍結對象可能會對文件大小產生負面影響。
有關更多信息,請參見此處

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