Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Make Room DAO return LiveData<Cursor>

Do you know how to make Room DAO return a LiveData<Cursor> ?


I need to request a lot of objects from my DB.
Because of memory issues, I don't afford an heavy object list, I need a Cursor.
Of course data could be updated and I need to be notify when it occurred.
So a LiveData<Cursor> should be a good solution.

But, when I compile this :

@Dao
public interface FooDao {

    @Query("SELECT * FROM foo")
    LiveData<Cursor> getFoo();

}

Android Studio says to me : Error:(22, 22) error: Not sure how to convert a Cursor to this method's return type

Well... Please do not tell me we can't get notify of data updates with a Cursor :/

like image 663
Zxcv Avatar asked Sep 03 '25 02:09

Zxcv


1 Answers

Thanks to @pskink in comments, here is my solution:

public class DbLiveData<T>
        extends MutableLiveData<T> {

    public interface DataLoader<T> {

        T loadData();

    }

    @NonNull
    private final InvalidationTracker.Observer mDatabaseObserver;

    @NonNull
    private DataLoader<T> mDataLoader;

    @NonNull
    private final Executor mExecutor;

    public DbLiveData(@NonNull String[] tables, @NonNull Executor executor, @NonNull DataLoader<T> dataLoader) {
        mExecutor = executor;
        mDataLoader = dataLoader;
        mDatabaseObserver = new InvalidationTracker.Observer(tables) {
            @Override
            public void onInvalidated(@NonNull Set<String> tables) {
                loadData();
            }
        };
        loadData();
    }

    @Override
    protected void onActive() {
        super.onActive();
        DB.get() // I get my DB from a Service Locator
                .getInvalidationTracker()
                .addObserver(mDatabaseObserver);
    }

    @Override
    protected void onInactive() {
        super.onInactive();
        DB.get() // I get my DB from a Service Locator
                .getInvalidationTracker()
                .removeObserver(mDatabaseObserver);
    }

    private void loadData() {
        mExecutor.execute(
                () -> postValue(mDataLoader.loadData())
        );
    }

}

I created a LiveData which manage an InvalidationTracker.Observer. You just have to pass a DataLoader to constructor, this one have to get the Cursor from the database.
Like this :

new DbLiveData<Cursor>(
    new String[] { "foo" },
    getExecutor(), // Provide an Executor
    () -> DB.get() // I get my DB from a Service Locator
            .fooDao()
            .getFoo()
)
like image 138
Zxcv Avatar answered Sep 07 '25 07:09

Zxcv