Understanding Realm Live Objects and Change Notifications
Learn how Realm’s live objects auto‑update across threads, how to subscribe to changes with notification tokens, and the practical steps to verify and avoid common pitfalls.
22 Apr 2026, 17:31 UTC

The problem: stale data after a background write
Imagine you have a list of tasks displayed on the screen. A worker thread updates a task’s status by writing to Realm. If your UI code reads the task object before the write completes, you might see the old value and have to manually refresh the query. Realm’s live‑object model removes that manual step, but it also introduces nuances you need to handle correctly.
How Realm objects stay live
When you obtain a Realm object (e.g., Task task = realm.where(Task.class).equalTo("id", 1).findFirst();), the object is not a copy. It holds a reference to the underlying data in the Realm file. Any write transaction—whether it runs on the UI thread, a background thread, or a worker pool—modifies that shared data directly. Because the object points to the same memory, reading its properties after the transaction commits instantly reflects the new value, without requiring a re‑query.
This behavior is thread‑confined: you must read the object on the same thread where you obtained it, or you must use a thread‑safe copy (realm.copyFromRealm(task)) if you need to pass it across threads.
Subscribing to changes with notification tokens
Live objects let you see the latest data, but UI frameworks often need to react to changes (e.g., reload a RecyclerView). Realm provides a publish‑subscribe mechanism:
- Attach a listener to a Realm, a
RealmResultscollection, or a single object. - The listener receives a callback on the thread where it was added.
- Multiple changes inside the same write transaction are coalesced into a single notification, reducing UI jitter.
Example: listening to a collection of tasks.
// UI thread
Realm realm = Realm.getDefaultInstance();
RealmResults<Task> tasks = realm.where(Task.class).findAllAsync();
RealmChangeListener<RealmResults<Task>> listener = tasks -> {
// tasks is already updated; refresh UI
taskAdapter.updateData(tasks);
};
tasks.addChangeListener(listener);
// Remember to remove it later
// tasks.removeChangeListener(listener);
The listener fires exactly once after the background write transaction commits, even if several properties of several objects changed in that transaction.
Practical verification steps
- Open a Realm on the UI thread and query a collection.
- Add a collection listener that increments a counter in its callback.
- On a background thread, open the same Realm, begin a write transaction, modify an object, and commit.
- After the transaction finishes, assert that the listener’s counter increased by exactly one.
- Read the modified object directly on the UI thread (no re‑query) and confirm its property matches the new value.
- Call
token.invalidate()on the listener token, perform another write, and verify the counter does not change.
These steps confirm that live updates work, that notifications are coalesced, and that invalidating a token stops further callbacks.
Trade‑offs and limitations
Live objects are powerful, but they can surprise developers who expect snapshot semantics:
- Modifying a live object outside a write transaction throws
RealmException. You must wrap changes inrealm.beginTransaction()/commitTransaction(). - If you forget to remove a listener (especially on Android UI components), the listener holds a reference to the UI, preventing garbage collection and causing memory leaks.
- Because the object updates instantly, concurrent reads on different threads may see intermediate states if you rely on the object’s value before the transaction finishes on that thread. Always read after the transaction commits or use asynchronous queries.
- Obtain objects on the thread where you will read them.
- Use collection or object listeners for UI updates, and always invalidate them in the corresponding lifecycle method (e.g.,
onDestroyfor Activities,onViewDestroyedfor Fragments). - Validate behavior with the verification steps above before shipping.
Actionable closing
To leverage Realm’s live objects safely:
When these patterns are followed, Realm’s automatic updates eliminate boilerplate refresh code while keeping your UI responsive and consistent.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.