ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • ListAdapter, DiffUtil
    개발/안드로이드 2021. 8. 20. 09:24

    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;
            }

     

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

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

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

    댓글

Designed by Tistory.