I have a `LiveData` of type `MutableLiveData<Mu...
# android
n
I have a
LiveData
of type
MutableLiveData<MutableList<User>>
. I have an issue. If I update the
value
by another
MutableList<User>
the changes are emitted to the observers. However, if I simply add other elements to the
MutableList<User>
, that is already initialised in the
LiveData
, the changes are not emitted. Here is what I am doing...
Copy code
//other codes 
private val _users: MutableLiveData<MutableList<User>> = MutableLiveData<MutableList<User>>()

init {
    _users.value = mutableListOf<User>()
}

val uiScope = CoroutineScope(Dispatchers.Main + Job())

fun loadUsers(){
    uiScope.launch{
        // this works perfectly
        _users.value = fetchUsers() as MutableList<User>
        // this doesn't work... no idea why
        _users.value.addAll(fetchUsers())
    }
}

suspend fun fetchUsers(): List<User>{
    return withContext(<http://Dispatchers.IO|Dispatchers.IO>){
        //fetch users asynchronously
    }
}
This is just an emulation of the actual program. That actual code is not that trivial.
d
U need to use
setValue()
every time u want to dispatch new value to active observers. In second case you're doing nothing with the result. U could do something like:
Copy code
val newUsers = _users.value.addAll(fetchUsers)
_users.value = newUsers
n
@divid3d
_users.value = fetchUsers()
does indeed call the
setValue()
. Maybe that's why it is working. In the second case I am updating the
MutableList<User>
stored inside the
LiveData
. Does it have no effect because I am calling a
suspend
function? I am no sure whether your code would even type-check.
Copy code
val newUsers = _users.value.addAll(fetchUsers)
_users.value = newUsers
_users.value.addAll(fetchUsers())
would return a
Boolean
. And we can only store
MutableList<User>
inside
_users.value
.
d
Oh right. So its should look something like:
Copy code
val newUsers =_users.value.apply{
    addAll(fetchUsers)
}
_users.value = newUsers
a
Mutations are always troublesome for a reactive state. Try _users = _users.value + fetchedUsers Which will create a new object and that should actually be detected as such and send the update.
a
Agreed, this kind of setup is a bad habit to get into and will become a source of bugs down the road. Once you submit an object to be seen by observers you should stop mutating it.
n
@divid3d @Aniket Kadam either of the solutions works. However, effectively it involves reassigning the whole object inside the
LiveData
. Why is it like that? I have even tried this,
Copy code
// inside viewModel
// _users is a MutableLiveData<MutableList<User>>
fun loadUsers(){
    
    val uiScope = CoroutineScope(Dispatchers.Main + Job())
 
    uiScope.launch{
        _users.value.add(User())
    }

}
But it doesn't emit the value. In contrast, this code works,
Copy code
// inside viewModel
// _users is a MutableLiveData<MutableList<User>>
fun loadUsers(){
    _users.value.add(User())
}
Why does it work this way? Apparently,
LiveData
doesn't notify the observers about the changes while done asynchronously, even if it is the main thread. But it works when it is synchronous.
a
If observers are getting notified when you edit a list that way in your second example it's because you're getting lucky and some other side effect of your setup is responsible. LiveData has no way to observe changes to a list assigned to it.
👍 3
n
@Adam Powell @divid3d @Aniket Kadam thank you all for your help.
693 Views