Programming/Android

Recyclerview에서 ListAdapter 의 변경사항이 실시간으로 업데이트 되지 않던 이슈

YK Choi 2021. 11. 24. 21:22

지난 글에서 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

 

[Android] Recyclerview에 ListAdapter사용하기

지금까지 내가 안드로이드에서 리스트뷰를 구현했던 학습 과정은 아래의 순서였다. 기본 ListView Custom ListView RecyclerView DataBinding한 RecyclerView 이제 다음 단계는 ListAdapter를 이용한 RecyclerView..

yk-coding-letter.tistory.com

 

 

 

도움된 사이트

 

https://coderedirect.com/questions/355965/pagedlistadapter-does-not-update-list-if-just-the-content-of-an-item-changes

 

PagedListAdapter does not update list if just the content of an item changes - Code Redirect

I'm using the Room and Paging libraries to display categories.My Entity:@Entity(tableName = Database.Table.CATEGORIES)data class Category( @PrimaryKey(autoGenerate = true) @ColumnInfo(name...

coderedirect.com

https://bb-library.tistory.com/257

 

[안드로이드] ListAdapter의 작동 원리 및 갱신이 안되는 경우

개요 RecyclerView를 활용하여 목록을 리스팅할 때 흔히 사용하는 어답터로 RecyclerView.Adapter와 ListAdapter로 나뉜다. 전자는 아이템 목록을 직접 관리하며 값이 변경될 경우 변경된 범위, 항목에 대해

bb-library.tistory.com