Search code examples
androidrealm

Long Lived Background Instances of Realm


I've read the warnings about keeping long-lived Realm instances on background non-Looper threads. I've also seen the suggestion that opening and closing Realm instances quickly is not the best idea. Given those constraints (if either is invalid, let me know), I'm trying to identify the best way to use Realm with websockets, where websocket events contain information that requires access to the Realm.

Particularly, what is the right way to go between these options (or something else entirely?):

  • Open and close the Realm on every event
  • Open the Realm at the start of the thread, periodically (every 30s or so) begin and commit transaction on the thread to bring the Realm up to date.
  • Allocate a Looper for the thread that handles websocket messages. Create the Realm instance once on the thread, and leave it open for the thread's lifetime, using the Looper to keep it up to date.

Other things worth noting:

  • The client will already have a Realm open on the UI thread. So, as far as I understand, this means the background threads at least do not need to pay the price of schema validation.
  • There's no way to predict the frequency of websocket events. In the typical case, there may be no more than one or two events per second. However, in the case that a large operation happens on the server which changes a bunch of objects, the client may receive hundreds or thousands of websocket events fairly rapidly.

Solution

  • Open and close the Realm on every event

    Personally, I find that to be reliable according to the people who say "their Realm is out of date", so this only makes sense if you actually call refresh() after Realm.getDefaultInstance().

    Open the Realm at the start of the thread, periodically (every 30s or so) begin and commit transaction on the thread to bring the Realm up to date.

    Periodic updates would cause either needless transactions, or be "unaware of change" every now and then.

    Also, you actually don't need to commit the transaction to force an update, because beginTransaction() already brings the Realm to the new version.

    realm.beginTransaction();
    realm.cancelTransaction();
    

    This would be sufficient on its own for that - but technically, this is a workaround to replace using refresh() (not part of public API), with also blocking the thread if a transaction is open on another thread.

    Allocate a Looper for the thread that handles websocket messages. Create the Realm instance once on the thread, and leave it open for the thread's lifetime, using the Looper to keep it up to date.

    If you want to ensure at all times that the current thread is up to date reliably and also without blocking other threads in the process, then this is the way to go.


    So the two ways are either:

    1.) keep a newSingleThreadedExecutor() that does queries to the Realm only inside a transaction

    2.) keep a HandlerThread alive for as long as needed, and execute the web socket + query stuff there

    (Personally, I've only ever used HandlerThread with RxJava + AndroidSchedulers.from(handlerThread.getLooper())).