Search code examples
androidandroid-architecture-components

Observing changes in Room Database table using ItemKeyedDataSource


I have implemented class which extends ItemKeyedDataSource and provides paging data from room database's data access object (DAO). My DAO's query methods pass lists of data objects (not wrapped by LiveData) to DataSource callbacks.

What is the recommended way to invalidate DataSource after changes occur in it's wrapped database table, for example if changes come from background Service? How automatic data invalidation is implemented in DataSource.Factory<Integer, T> return parameter that DAOs can generate?


Solution

  • Automatic DataSource invalidation can be implemented by hooking InvalidationTracker.Observer to InvalidationTracker. You can get InvalidationTracker instance from getInvalidationTracker().

    I implemented my InvalidationTracker.Observer like this:

    public class DataSourceTableObserver extends InvalidationTracker.Observer {
    
        private DataSource dataSource;
    
        public DataSourceTableObserver(@NonNull String tableName) {
            super(tableName);
        }
    
        @Override
        public void onInvalidated(@NonNull Set<String> tables) {
           if (dataSource != null) dataSource.invalidate();
        }
    
        public void setCurrentDataSource(DataSource source) {
            dataSource = source;
        }
    
    }
    

    And I'm using it in my inner DataSource.Factory class like this:

    public static class Factory implements DataSource.Factory<TvProgram, TvProgram> {
    
        private Context appContext;
        private DataSourceTableObserver observer;
        private InvalidationTracker tracker;
        private int channelId;
    
        public Factory(Context context, int channelId) {
            appContext = context.getApplicationContext();
    
            observer = new DataSourceTableObserver(AppDatabase.PROGRAMS_TABLE);
            tracker = AppDatabase.getInstance(appContext).getInvalidationTracker();
            tracker.addObserver(observer);
    
            this.channelId = channelId;
        }
    
        @Override
        public DataSource<TvProgram, TvProgram> create() {
            EpgDataSource epgDataSource = new EpgDataSource(appContext, channelId);
            observer.setCurrentDataSource(epgDataSource);
            return epgDataSource;
        }
    
        public void cleanUp() {
            tracker.removeObserver(observer);
            observer = null;
        }
    
    }
    

    When DataSourceTableObserver invalidates DataSource, it's Factory inner class creates new DataSource instance with newest data.