지난 글에서 ListAdapter를 포스팅하며 DiffUtil에서 새로운 아이탬을 구별하거나, 아이탬의 내용이 같은지 구분하는 것을 설명한 적이 있다
해당 링크 : https://yk-coding-letter.tistory.com/8
문제 상황
그런데 최근, 개발을 하던 중 Room에서 변경사항을 recyclerview에 실시간으로 업데이트하려고 했으나 잘 작동하지 않았던 이슈가 있었다.
분명히 areItemsTheSame() 과 areContentsTheSame()을 제대로 작성했음에도 ui로 드러나지 않았고, 앱을 껐다 키거나 스크롤을 내렸다가 다시 돌아왔을 때야 비로소 변경되었다.
어떻게 이런 일이 발생할 수 있는 것인지 생각해보았다.
우선 나는 정확히는 Paging3의 recyclerview를 사용하고 있었다. Paging3에서는 정말 많은 기능을 지원하고 있었고, 단순 Recyclerview보다는 디테일한 프로그래밍에 대해서는 약간의 아쉬움을 느끼고 있었다.(예를 들면 일단 Paging을 쓰지 않는 Recyclerview + ListAdapter는 데이터가 새로 올때마다 submitList하는데, Paging에서는 데이터를 Flow로 받아와 여러번의 api 콜이 있어도 스트림으로 받아와 submitList는 한번만 실행된다는 점이 그렇다. 장점이자 단점인 것 같다.)
아무튼 그래서 이게 Paging이라서 일반 RecyclerView와는 다른 무언가를 설정이 있어야하나? 이란 생각을 하게 되었고 Paging을 잠깐 의심하게 되었다.
원인
그러던 중 ListAdapter에서 내부작동원리를 들여다보게 되었고,
submitList의 구현 코드에서 아래와 같이 두 List가(oldList와 newList) == 인지 비교하는 Java 코드를 발견하게 되었다.
Kotlin과 다르게 Java에서의 == 는 그 값 자체가 아니라 주소값을 비교한다.
값 자체를 비교하려면 Equals() 를 사용해야했다.
이제서야 내 코드를 돌아보니 아래와 같은 문제가 있었다.
- remote은 PagingData<Notice> 이고 서버로 부터 받아온 Notice 데이터들이 리스트로 저장되어 있다.
- local은 room을 통해 가져온 Notice 데이터들의 리스트이다.
remote의 각각의 데이터에 대해서 isRead항목만 true로 값을 바꾸고 있었다. 이러면 참조값이 변경되지 않는다.
(Repository 에 해당하는 클래스)
remote.map {
if (local.contains(it.articleId)) {
it.isRead = true
return@map it
} else {
return@map it
}
}
해결
따라서 아래와 같이 수정하였다.
새로운 Notice 객체를 생성한 것이다.
remote.map {
if (local.contains(it.articleId)) {
return@map Notice(
postedDate = it.postedDate,
subject = it.subject,
category = it.category,
url = it.url,
articleId = it.articleId,
isNew = it.isNew,
isRead = true,
isSubscribing = it.isSubscribing,
tag = it.tag
)
} else {
return@map it
}
}
요약
ListAdapter에 사용되는 데이터를 submitList 하기 전에 값을 단순히 변경(modify)시키는 것은
Recyclerview에서 새로운 데이터가 들어오는
areItemsTheSame
와 데이터의 변경을 구분하는
areContentsTheSame
에 영향을 주지 못한다.
ListAdapter에 변경사항을 알리려면 새로운 해당 객체를 생성해야 한다.
돌아보니 이전에도 같은 이슈를 겪었었다. 그땐 알지 못했지만 요 아래 링크에서 마지막 쪽에 .toList()를 붙이기 전에 어떤 원리로 문제가 발생했는지 이제는 이해가 된다.
https://yk-coding-letter.tistory.com/8
도움된 사이트
https://bb-library.tistory.com/257
'Programming > Android' 카테고리의 다른 글
메모리 누수 이슈 기록 (LocationListener) (0) | 2022.01.30 |
---|---|
[Android] 첫 오픈소스 라이브러리 배포 - HoldableSwipeHandler 원리 및 출시 후기 (8) | 2022.01.03 |
[Android] App 코드 난독화와 Gson으로 json 파싱이 안되는 이슈 (0) | 2021.11.16 |
'Open Source License' Activity plugin 적용하기 (0) | 2021.10.14 |
RxJava + Retrofit 으로 통신 모듈 구현하기 (4) | 2021.10.03 |