Search

'ListAdapter'에 해당되는 글 1건

  1. 2021.08.20 ListAdapter, DiffUtil

ListAdapter, DiffUtil

개발/안드로이드 2021. 8. 20. 09:24 Posted by 아는 개발자

ListAdapter는 RecyclerView.Adapter의 확장기능으로 리스트내에 노출할 아이템의 변경 여부를 백그라운드 쓰레드에서 판단할 수 있는 기능을 제공한다. 생성자에는 DiffUtil.ItemCallback의 구체 클래스를 넘겨주는데 여기서 구현하는 두 함수를 이용해 아이템 변경 유무를 판단한다

 

class BookAdapter() : ListAdapter<Book, RecyclerView.ViewHolder>(object : DiffUtil.ItemCallback<Book>() {
    override fun areItemsTheSame(oldItem: Book, newItem: Book): Boolean {
        return oldItem.isbn13 == newItem.isbn13
    }

    override fun areContentsTheSame(oldItem: Book, newItem: Book): Boolean {
        return oldItem == newItem
    }
}

 

areItemsTheSame 은 두 아이템이 같은 것인지 확인한다. 아이템마다 고유의 id 가 있다면 두 아이템은 같은 것으로 볼 수 있다. 예제처럼 책을 리스트로 노출한다면 isbn이 동일할 때 같은 책인 것으로 볼 수 있다.

 

areContentsTheSame 은 같다고 알려진 두 아이템의 변경 유무를 확인 한다. return 값이 true면 동일한 것이고 false면 변경이 일어난 경우다. 같은 id 를 가졌더라도 클래스 내부의 값이 달라졌다면 변경이 된 것이기 때문에 다른 것으로 본다. 코드상에서처럼 두 객체를 비교해도 되고 성능을 올리려면 변경유무를 판단할 때 사용하는 속성값만 넣어도 된다. 책의 경우에는 책 제목이나 가격 속성을 이용해도 좋을 것 같다.

 

최종적으로 areContentsTheSame 이 false 인 item에 대해서만 onBindVieholder 함수가 호출 된다. Bind 함수 내에 많은 작업을 처리하는 경우 성능 개선에 도움이 될 것 같다.

 

주의사항

 

https://stackoverflow.com/a/50031492

 

ListAdapter not updating item in RecyclerView

I'm using the new support library ListAdapter. Here's my code for the adapter class ArtistsAdapter : ListAdapter<artist, artistsadapter.viewholder="">(ArtistsDiff()) { override fun</artist,>

stackoverflow.com

 

submitList로 리스트를 업데이트 할 때는 리스트 주소값도 바뀌어야 한다. ListAdapter에서 DiffUtil로 사용하는 AsyncListDiffer 클래스는 새로운 리스트가 기존 리스트와 같은 객체인 경우에는 값을 업데이트하지 않는다. 이는 곧 내부에 데이터를 아무리 바꿔도 리스트의 주소값이 동일하면 변경된 값을 화면에 노출시키지 않는다는 의미다. 이럴때는 깊은 복사(list deep copy)를 사용해 완전히 새로운 객체를 만드는 수밖에 없는데, 리스트 내 객체를 빼거나 추가하는 경우가 아니면 리스트 내부 객체까지 깊은 복사를 해야 리스트의 깊은 복사가 된다. 

 

public class AsyncListDiffer<T> {
    private final ListUpdateCallback mUpdateCallback;
    ...
    @SuppressWarnings("WeakerAccess") /* synthetic access */
    public void submitList(@Nullable final List<T> newList,    
            @Nullable final Runnable commitCallback) {
        // incrementing generation means any currently-running diffs are discarded when they finish
        final int runGeneration = ++mMaxScheduledGeneration;

        if (newList == mList) {
            // nothing to do (Note - still had to inc generation, since may have ongoing work)
            if (commitCallback != null) {
                commitCallback.run();
            }
            return;
        }

 

성능을 위한 선택이었겠지만 내부 데이터 변경도 볼 수 없는 어댑터는 사용성이 크게 떨어지는 것 같다.

728x90

'개발 > 안드로이드' 카테고리의 다른 글

RxJava - Disposable Deep Dive!  (0) 2021.09.17
RxJava dispose()  (0) 2021.09.16
ListAdapter, DiffUtil  (0) 2021.08.20
Coroutine + Retrofit | Coroutine + Room  (0) 2021.07.22
suspend fun  (0) 2021.07.22
Single, Maybe, Completable  (0) 2021.07.04