Live UI Updates with Realm Notification Tokens in Android
Learn how Realm's notification tokens automatically update Android UI components, eliminating manual refresh code while highlighting UI‑thread considerations and cleanup practices.
06 Jan 2026, 00:50 UTC

The Problem: Stale Lists and Boilerplate Refresh Code
When a mobile app stores data in Realm, every write transaction can change the underlying results. If the UI is not told about those changes, developers end up writing repetitive code to query the database again and call notifyDataSetChanged() on adapters. This approach is error‑prone and can leave the screen showing outdated information.
Thesis: Realm’s Live Object Notifications Eliminate Manual Refreshes
Realm provides change listeners—notification tokens—that deliver callbacks whenever a query result or a single object is modified. By attaching a token to a RealmResults collection, the UI can update automatically as soon as the write transaction commits, removing the need for manual refreshes.
Worked Example: Binding a RecyclerView to RealmResults
Assume we have a simple Person model with name and age fields. The steps below show how to set up a notification token in an Android Activity or Fragment.
- Open a Realm instance on the UI thread (or any thread you plan to deliver callbacks on).
- Obtain a
RealmResults<Person>query, for example all persons sorted by name. - Create a
RealmChangeListenerthat receives the updated results and passes them to the adapter. - Add the listener with
addChangeListener, which returns aRealmNotificationToken. - In
onDestroy(or the fragment’s lifecycle method), callremoveChangeListenerorremoveAllChangeListenersto avoid leaks.
class PersonListFragment : Fragment() {
private lateinit var realm: Realm
private lateinit var adapter: PersonAdapter
private var token: RealmChangeListener<RealmResults<Person>>? = null
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
// 1. Open Realm – assumes the fragment lives on the main thread
realm = Realm.getDefaultInstance()
// 2. Query for all Person objects
val results: RealmResults<Person> = realm.where(Person::class.java)
.findAllSortedAsync("name")
// 3. Set up RecyclerView adapter
adapter = PersonAdapter(emptyList())
binding.recyclerView.adapter = adapter
// 4. Attach notification token
token = { results, changeSet ->
// The callback runs on the thread where Realm was opened (main thread here)
val updatedList = results.asFlowable().toList() // simple copy for adapter
adapter.submitList(updatedList)
}
results.addChangeListener(token!!)
}
override fun onDestroyView() {
super.onDestroyView()
// 5. Remove token to prevent leaks
token?.let { results.removeChangeListener(it) }
realm.close()
}
}
Each time a write transaction commits—whether from this fragment, a background service, or another process—the callback receives the updated RealmResults and the adapter refreshes its list. No explicit notifyDataSetChanged calls are needed inside the transaction code.
Trade‑off: UI‑Thread Delivery and Potential Overhead
Notification callbacks are invoked on the same thread where the Realm instance was opened. In the example above, that is the main (UI) thread. If your app performs many fine‑grained writes—such as inserting hundreds of objects in a tight loop—each transaction will trigger a separate callback, potentially causing UI jank or dropped frames.
To keep the UI responsive you can:
- Coalesce writes: group multiple object creations or updates into a single transaction using
realm.executeTransaction. - Use fine‑grained payloads: inspect the
OrderedCollectionChangeSetargument to know exactly which indices changed and callnotifyItemRangeInserted,notifyItemRangeRemoved, etc., instead of rebuilding the whole list. - Open the Realm on a background thread and use
HandlerorrunOnUiThreadto marshal the callback back to the UI thread if you prefer to keep heavy work off the main thread.
These strategies let you enjoy automatic updates while limiting the work done on the UI thread.
Practical Verification Steps
- Run the app on an emulator or device.
- Open the Android Studio Logcat and add a log statement inside the notification lambda (e.g.,
Log.d("Realm", "Callback fired, size=${results.size}")). - Trigger a write—such as tapping a FAB that inserts a new
Person—and observe the log line appear. - Watch the RecyclerView animate the new item without calling
notifyDataSetChangedmanually. - After navigating away from the fragment, use Android Studio’s Profiler → Memory to confirm no Realm instances remain leaked; the token removal and
realm.close()should release the reference.
If the log never appears, check that the Realm was opened on the same thread delivering the callback and that the token was not removed prematurely.
Closing: Make Live Updates a Default Pattern
Realm’s notification tokens turn a common source of boilerplate into a declarative UI‑data binding. By attaching a listener to a query result and cleaning it up in the component’s lifecycle, you get real‑time lists with minimal code. Remember to watch for UI‑thread load, coalesce or fine‑grain your writes, and always detach the token to avoid memory leaks. With those precautions, live updates become a reliable, low‑maintenance part of your mobile architecture.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.