Wrapping MutableList in either LiveData or MutableLiveData

11.1k views Asked by At

What is the recommended means of wrapping a MutableList within either LiveData or MutableLiveData such that one can modify the list and observe the changes it undergoes ?

I have some fairly short tables (<20 items) in Room (It caches the values retrieved from a server). I would like to retain a mutable subset of these items; to be used as filters in queries for other, larger, tables. My thinking was to assign a MutableList to the model class in my application and make it observable by wrapping this class in either MutableLiveData or LiveData. Then by updating the items in these selections I could trigger the necessary queries on the large tables.

Presently my code has the following structure, I've stripped it down in my question to what I thought was relevant. Eventually I would like to use multiple keyset in more complex queries. My simplified model file is as follows,

class Model(application : Application) : AndroidViewModel(application)
{
  val keys    : LiveData<List<Key>>
  val keyset  : LiveData<MutableList<Key>>
  val values  : LiveData<List<Value>>
  init {
    keys   = repository.keys
    values = repository.values
    keyset = mutableListOf<Key>() // I'm not entirely sure how to instantiate LiveData<MutableList<Key>>
  }
}

it relies upon the repository,

class Repository(private val queryset : QuerySet)
{
   val keys   : LiveData<List<Key>>   = queryset.getKeys()
   val values : LiveData<List<Value>> = queryset.getValues()
}

Which relies upon the queryset

@Dao
class QuerySet(application : Application) : AndroidViewModel(application)
{
  @Query("SELECT * FROM KeyEntity")
  fun getKeys() : LiveData<list<Key>>
  @Query("SELECT * FROM ValueEntity WHERE key in (:keys)")
  fun getValues(keys : MutableList<Key>) : LiveData<list<Value>>
}

The Key and Value classes are simply the POJO for KeyEntity and ValueEntity and simply specify the same fields.

Should I be using MutableLiveData or LiveData for this purpose ? If so how should one implement keyset ? Perhaps there is a more canonical manner for doing so ?

2

There are 2 answers

1
Carel On

LiveData vs. MutableLiveData

I was not sure whether I had to wrap my MutableList as LiveData<MutablList<POJO>> or as MutableLiveData<MutableList<POJO>>, where POJO is an instance of either a database entity or a data class proxying one or more database entities. Sneh Pandaya succinctly explains the difference between the two classes. I was also unsure as to how to instantiate them. The following creates the instance but the value of keyset is initialized as null and one must set an initial value for the MutableLiveData.

class Model(application : Application) : AndroidViewModel(application)
{ ...
  val keyset  : LiveData<MutableList<Key>>
  init { ...
    keyset = MutableLiveData<MutableList<Key>>() // Initialized as null
    keyset.value = mutableListOf<Key>()          // Assign an empty list
  ... }
... }

There is a nuance in MutableLiveData, it does not formally encapsulate the data. That is when it wraps an item it does not expose the wrapped items methods as it's own emitting notifications as the wrapped item is modified. It merely tracks when the wrapped item has been swapped out (See Gznlt). Samnang CHEA provides a nice method for supporting this.

Location, Location, Location, ...

I was unsure of where to formally place the keyset attribute. The Android documentation provides the following information; as it turns out I was on the correct path.

Update LiveData objects

LiveData has no publicly available methods to update the stored data. The MutableLiveData class exposes the setValue(T) and postValue(T) methods publicly and you must use these if you need to edit the value stored in a LiveData object. Usually MutableLiveData is used in the ViewModel and then the ViewModel only exposes immutable LiveData objects to the observers.

Queries

What I have not worked out yet is how to trigger an update of the values.

2
Hamza Mehboob On

The Observer is notified only on Changes to the Value of LiveData

This is true for both LiveData and StateFlow

When you declare instance of MutableLiveData<MutableList<Any>>, and then you add value to the list wrapped inside MutableLiveData as variable.value.add(Any), the observer would not be notified. It does not listen to such changes where the internal content of the list/model is updated.

There are different workarounds to make the observer notified by creating Extensions but that's quite complication to achieve the simplest task.

The simplest way is as follows:

private val _mobileTopUp = MutableLiveData<List<DataModel>>(emptyList())

Then create a simple Mutable List where you will collect the items. You can declare this list on top of your function so it gets empty everytime you make function call to avoid redundant data or not. That totally depends on you

val list = mutableListOf<DataModel>()
list.add(it)
_mobileTopUp.value = list

Now, the observer will be notified because we are setting list as value of LiveData here.