ListAdapter를 사용 중 submitList()
를 통해 새로운 데이터를 넣어도 실시간 데이터 업데이트가 되지 않는다.
기본적으로 DiffUtill은 areItemsTheSame
에서 true
를 반환해야 areContentsTheSame
메소드를 실행하고, 거기서 false
를 반환하면 데이터가 변경되었다고 판단, UI 업데이트가 이루어진다.
이를 활용하여, oldItem
의 내용과 newItem
의 내용을 로그에 담아 비교해보기로 했다. 코드는 아래와 같다.
val myDiffCallBack = object : DiffUtil.ItemCallback<TaskItem>() {
override fun areItemsTheSame(oldItem: TaskItem, newItem: TaskItem): Boolean {
Log.d(TAG, "areItemsTheSame: ${oldItem.task.id == newItem.task.id}")
return oldItem.task.id == newItem.task.id
}
override fun areContentsTheSame(oldItem: TaskItem, newItem: TaskItem): Boolean {
Log.d(TAG, "areContentsTheSame: ${oldItem.task.content} : ${newItem.task.content} + ${oldItem == newItem}")
return oldItem == newItem
}
}
그리고 결과가 위와 같이 나왔다. 원인은, oldItem
의 값에 새로 넣은 값, 즉 newItem
의 값이 들어가며 결과가 true
가 반환되는 것이 원인이었다.
원래대로라면, oldItem
의 값에는 321321456
이 들어가야 하는 것이 맞다.
StackOverFlow 에서 해결책을 여러가지로 찾아보았으나, 결론부터 말하자면 완전한 해결책은 없었다. 😱
그나마 이해가 되는 답변은 아래와 같다.
DiffUtill 은 기본적으로 oldItem
을 복사하는 것이 아니라, 참조하여 저장한다. oldItem
이 Mutable
, 즉 변경 가능한 객체라면 데이터가 변경될 때 oldItem
도 변경되어 올바른 비교가 불가능한다는 답변이다.
이에 대해 완전한 해결책은 찾지 못했으나, 성능 저하가 우려되지만 어느정도 오류를 해결할 수 있는 방법을 찾았다.
우선 첫번째 방법은 아래와 같다. (References : StackOverFlow)
listViewModel.tasks.observe(viewLifecycleOwner) {
taskListAdapter.submitList(null)
taskListAdapter.submitList(it.toMutableList())
}
처음 전달 값에 아예 null
을 줘버리고, 그 다음에 변경된 리스트를 주는 것인데, 사실 이러면 전체 값이 변경되므로 ListAdapter 를 쓰는 이유가 없다. 심지어, 변경 후엔 화면 깜빡임까지 발생한다.
그리고 여러 방안을 시도해봤으나 해결이 되지 않던 와중, 그나마 타협할 수 있는 방법을 발견했다.
바로, areContentsTheSame
메소드에서 리턴값을 전부 false
로 줘버리는 것이다. [..]
이게 맞는 방법인지는 잘 모르겠지만, 일단 데이터 변경을 감지하면 변경된 데이터를 넘기는 식으로 코드가 구성되어 있기 때문에 이 방법을 채택했다.
val myDiffCallBack = object : DiffUtil.ItemCallback<TaskItem>() {
override fun areItemsTheSame(oldItem: TaskItem, newItem: TaskItem): Boolean {
return oldItem.task.id == newItem.task.id
}
override fun areContentsTheSame(oldItem: TaskItem, newItem: TaskItem): Boolean {
return false
}
}
일단 기대한대로 결과는 나오기는 하지만, 완전한 해결책은 아니라고 생각한다. 어느정도 봉합을 했다는 것에 만족하도록 하자.
혹시 이 글을 보고 해결책을 아시는 분이 계시다면 공유 부탁드립니다 😰