Livedata with a raw query getAll() : how to update liveData's data source via ui (and therefore change UI accordingly)?

I have this Room+repository+LiveData+Viewmodel based app.
The room db consist of following classes:

  • a basic Note entity (long id, String data)
  • a db singleton for creating a roomdb
  • a dao with @insert / @update/ @delete annotation functions. It also has a @RawQuery annotated function getALL() that takes a query and returns a livedata .
@Dao
public interface TeaDao {

    @RawQuery(observedEntities = Tea.class)
    LiveData<List<Tea>> getALL(SupportSQLiteQuery query);
    ...
}

Any method which calls this function generates the query using this utility function:


// "SELECT * FROM table order by <colname>" OR
// "SELECT * FROM table where COL_FAVOURITE=1 order by <colname>"

public static SupportSQLiteQuery getSortedQuery(String sortByColname, boolean showOnlyFavorites) {

    SupportSQLiteQueryBuilder builder =
            SupportSQLiteQueryBuilder
                    .builder(DbUtils.Names.TABLE_NAME)
                    .columns(new String[]{"*"})
                    .orderBy(sortByColname);
    if (showOnlyFavorites) {
        builder.selection(Names.COL_FAVORITE + " = ? ", new Object[]{"1"});
    }

    SupportSQLiteQuery query = builder.create();
    //Log.e("DB_UTILS", "getSortedQuery:query="+query.getSql());
    return query;
}

  • a repo class for creating db and calling performing db operations using dao
  • a viewmodel class for holding repo instance and calling repo’s functions( that perform db operations)
  • main activity and some other ui activities

My problem is that i couldn’t figure our how to make this structure work with my ui . See, my ui would consist of a recycler view with some menu buttons . At first, my app can show default data with no filters . I can add the observer to Livedata<List> recieved on calling getAll() from VM , and populate my recycler view in its onChanged.

But I want that when user presses a button say “show favourites” , the query Select * from table where favourites=1 is processed via getAll(), and the result of this query is automatically passed to the previously set Livedata<List> , which triggers its onChanged() and therby my ui changes to a filtered version of results.

How can i do that?

How can i do that?

As far as I know, Room does not support that.

What you can do is:

  • Have the ViewModel use a MediatorLiveData<List<Tea>> as what it exposes to the UI layer
  • Have the ViewModel add and remove sources from that MediatorLiveData based upon filtering rules

I take that approach in the ToDo sample app from Exploring Android: https://gitlab.com/commonsguy/cw-andexplore/blob/v0.7/T32-Filter/ToDo/app/src/main/java/com/commonsware/todo/ui/roster/RosterMotor.kt

Basically, you rely on Room’s automatic-updating for places where you change the database contents (e.g., insert, delete). You switch what query you are observing via the MediatorLiveData.

I think i am getting what you are saying, sir. I read the code of RostorMotor.kt, but am currently not too good in kotlin. can you please help me with some java version of that code? (I tried decoding it to bytecode, but that got converted into some 16000 lines)

That class uses Kotlin coroutines in the save() function, so I cannot convert it to Java in isolation.

Using the Warescription site’s full-text search, here are some chapters with Java examples of MediatorLiveData:

Thank you so much! after 3 days of pain my code is finally working :-’’’)
I am now exposing a mediator livedata<list> instead of livedata instead of livedata<list> and changing the source to a filtered query result on button click. everything is working fine and beautifully, thanks!