Room数据库ItemKeyedDataSource失效方法及Factory自动实现问询
Great question! Let's break this down into two clear parts since you're asking about both custom ItemKeyedDataSource implementations and Room's built-in DataSource.Factory behavior.
1. Recommended way to invalidate a custom ItemKeyedDataSource when Room tables change (e.g., from a background Service)
Since your DAO returns raw lists (not wrapped in LiveData), you won't get automatic change observation out of the box. To handle table updates from background operations, you'll need to leverage Room's InvalidationTracker to detect changes and trigger data source invalidation manually. Here's how to do it properly:
Step 1: Register an
InvalidationTracker.Observerin your DataSource
TheInvalidationTrackeris Room's built-in mechanism to monitor changes to specific database tables. Create an observer that listens to the table(s) your DataSource queries, and callinvalidate()when changes are detected.Step 2: Clean up the observer when the DataSource is invalidated
Always remove the observer in theinvalidated()method to avoid memory leaks, since DataSources can be discarded when pagination state changes.
Here's a concrete code example (Kotlin):
class CustomItemKeyedDataSource( private val myDao: MyDao, private val appDatabase: AppDatabase ) : ItemKeyedDataSource<Int, MyEntity>() { // Observer that listens for changes to "my_entity_table" private val tableObserver = object : InvalidationTracker.Observer("my_entity_table") { override fun onInvalidated(tables: MutableSet<String>) { // Trigger data source invalidation when the table changes invalidate() } } init { // Register the observer when the DataSource is created appDatabase.invalidationTracker.addObserver(tableObserver) } override fun invalidated() { super.invalidated() // Unregister the observer to prevent memory leaks appDatabase.invalidationTracker.removeObserver(tableObserver) } // Implement your pagination methods using the DAO's raw list queries override fun loadInitial(params: LoadInitialParams<Int>, callback: LoadInitialCallback<MyEntity>) { val initialData = myDao.getInitialPage(params.requestedLoadSize) callback.onResult(initialData) } override fun loadAfter(params: LoadParams<Int>, callback: LoadCallback<MyEntity>) { val nextPageData = myDao.getNextPage(params.key, params.requestedLoadSize) callback.onResult(nextPageData) } override fun loadBefore(params: LoadParams<Int>, callback: LoadCallback<MyEntity>) { // Implement if you need backward pagination, else return empty list callback.onResult(emptyList()) } override fun getKey(item: MyEntity): Int = item.id }
When your background Service modifies the my_entity_table, Room's InvalidationTracker will detect the change, trigger the observer's onInvalidated() method, and your DataSource will call invalidate(). This tells the PagedList that the data is stale, and it will automatically request fresh data from your DAO.
2. How Room's DataSource.Factory<Integer, T> handles automatic invalidation
When you define a DAO method that returns a DataSource.Factory<Key, Value> (like below), Room generates all the boilerplate code needed to handle automatic invalidation for you:
@Query("SELECT * FROM my_entity_table ORDER BY id ASC") fun getPagedFactory(): DataSource.Factory<Int, MyEntity>
Here's what's happening under the hood:
- Room's generated implementation of this factory creates a
DataSourcethat automatically registers anInvalidationTracker.Observerfor the table(s) involved in the query. - Whenever the monitored table(s) are modified (from any thread, including background Services), the
InvalidationTrackertriggers the observer, which callsinvalidate()on the DataSource. - The
PagedListdetects the invalidation and initiates a reload of the data, ensuring your UI always shows the latest content without any manual intervention from you.
This works because Room knows exactly which tables your query touches, so it can target the observer to only those tables. You don't need to write any extra code for this behavior—it's all handled by Room's generated code.
In short:
- For custom
ItemKeyedDataSourceimplementations, use Room'sInvalidationTrackerto manually listen for table changes and triggerinvalidate(). - For Room-provided
DataSource.Factoryinstances, automatic invalidation is built-in, thanks to Room's generated code that handles table monitoring.
内容的提问来源于stack exchange,提问作者Tadas

