LiveData를 사용할 때 변수가 아닌 변수의 Property만 변경된 경우, LiveData를 Observe 하는 Observer에게 notify 되지 않는다.
이때, Bindable을 활용하면 property가 변경되었을 때, notify 되도록 구현할 수 있다.
@Bindable
어노테이션을 붙인다.notifyPropertyChanged(BR.변수이름)
을 호출한다.import androidx.databinding.BaseObservable
import androidx.databinding.Bindable
open class Player(val name: String) : BaseObservable() {
@Bindable
private val cards = ArrayList<Card>()
fun add(newCards: List<Card>) {
cards.addAll(newCards)
notifyPropertyChanged(BR.cards)
}
fun getCards() = cards.toList()
fun reset() {
cards.clear()
notifyPropertyChanged(BR.cards)
}
}
property가 변경되면 observer에게 notify 하는 callback을 가진 MutableLiveData를 구현한다.
import androidx.databinding.Observable
import androidx.lifecycle.MutableLiveData
class CustomMutableLiveData<T: Any> : MutableLiveData<T>(){
private val callbacks = HashMap<Observable, Observable.OnPropertyChangedCallback>()
fun observeList(list: List<Observable>?) {
clearCallBack()
list?.forEach { observable ->
val callback = object: Observable.OnPropertyChangedCallback() {
override fun onPropertyChanged(sender: Observable, propertyId: Int) {
value = value
}
}
observable.addOnPropertyChangedCallback(callback)
callbacks[observable] = callback
}
}
override fun onInactive() {
super.onInactive()
clearCallBack()
}
private fun clearCallBack() {
callbacks.forEach { (observable, callback) ->
observable.removeOnPropertyChangedCallback(callback)
}
callbacks.clear()
}
}
class GameViewModel : ViewModel() {
private val _players: CustomMutableLiveData<List<Player>> by lazy {
CustomMutableLiveData<List<Player>>().also { customMutableLiveData ->
val initialPlayers = listOf<Player>()
customMutableLiveData.value = initialPlayers // 1. 값을 설정하고
customMutableLiveData.observeList(initialPlayers) // 2. observeList를 호출한다.
}
}
...
}
위와 같이 구현하면 Player 자체가 바뀌지 않았음에도, Player의 Observer에게 notify된다.
override fun onStart() {
super.onStart()
val playersObserver = Observer<List<Player>> { newPlayers ->
(binding.recyclerView.adapter as PlayerAdapter).updatePlayer(newPlayers)
}
viewModel.players.observe(viewLifecycleOwner, playersObserver)
}
또한 어떤 Player의 어떤 property가 바꼈는지 알 수 없기 때문에, notifyDataSetChanged()
를 호출해야 해서 비효율적이다.
class PlayerAdapter : RecyclerView.Adapter<PlayerAdapter.ViewHolder>() {
....
fun updatePlayer(newPlayers: List<Player>) {
if (players == newPlayers) {
notifyDataSetChanged()
return
}
players.clear()
players.addAll(newPlayers)
notifyDataSetChanged()
}
이전에 CustomMutableLiveData이 하던 Property 감지 역할을 ViewHolder로 옮긴다. 이렇게 할 경우, 모든 Player가 아닌, property가 변경된 Player만 호출되며, 바뀌지 않은 Players 리스트에 대한 notify가 발생하지 않는다.
ViewModel에서 사용하던 CustomMutableLiveData 대신 기본 MutableLiveData로 변경한다.