ANDROID/Android Jetpack

[Android/Jetpack] Recyclerview Adpater 대신 ListAdapter 적용하기

주 녕 2021. 9. 27. 00:58
728x90

https://developer.android.com/

 

Room + LiveData를 이용한 MVVM 패턴 실습을 진행하던 중,

RecyclerView의 Adapter에 ListAdapter를 적용하는 예제를 참고하게 되어 보다 자세히 알아보기 위해 포스팅 하게 되었습니다!

ListAdpater는 구글 I/O 2018 Android Jetpack: what’s new in Android Support Library 세션에서 언급된 RecyclerView의 새로운 기능으로 소개되었습니다. 지금까지 그냥 Adapter를 사용하고 있었는데 너무 늦게 효율적인 방법을 찾은 것 같네요😓

 

기존 Adapter

RecyclerView에서 Adapter가 하는 일은

  • ViewHolder 객체 생성
  • 해당 객체에 데이터 리스트를 주입
  • 데이터 리스트의 변경을 UI에 반영 → notifyDataSetChanged()를 사용

🚩 여기서 발생하는 문제!

  1. 데이터를 업데이트 하는 곳마다 nofityDataSetChange()를 사용하여 매우 번거로움
  2. 하나의 데이터만 변경되어도 데이터 리스트 자체를 업데이트하므로 데이터의 양이 많은 경우 그만큼 지연시간이 발생한다.

🤔 이것을 해결하기 위한 방법? 

모든 리스트를 업데이트하는 방식 말고, 리스트의 차이를 계산하는 DiffUtil을 사용하자!

 

DiffUtil

'기존 데이터 리스트'와 '업데이트된 데이터 리스트'를 비교해서 차이를 알아내는 유틸리티 클래스

DiffUtil은 Eugene W. Myers의 difference 알고리즘을 사용하여 한 목록을 다른 목록으로 변환하기 위한 최소 업데이트 수를 계산한다고 합니다. 따라서 전체를 변경하는 것이 아닌, 변경되어야 하는 데이텀나 빠르게 업데이트 하는 역할을 합니다.

 

ListAdapter

RecyclerView.Adapter를 베이스로 한 클래스

AsyncListDiffer의 wrapper 클래스로 RecyclerView.Adapter<VH>를 구현하고 있습니다.

ListAdapter<데이터 클래스, 뷰홀더>(콜백)

 

ListAdapter에서는 내부적으로

  •  AsyncListDiffer를 사용해 간단하게 백그라운드 스레드에서 실행한 후 메인 스레드에 처리하기 위한 처리
  • List(불변)로 다루기 때문에 전달된 리스트의 항목을 직접적으로 변경하지 않고 변경된 List를 전달해야 함

따라서, 백그라운드 스레드에서 리스트 변경 사항이 계산되면 사용자는 업데이트된 Recyclerview를 볼 수 있는 것입니다!

ListAdpater에서사용할 수 있는 메서드는

  • getCurrentList() : 현재 리스트 반환
  • onCurrentListChanged() : 리스트가 업데이트 되었을 때 실행할 콜백 지정
  • submitList(val list: List<T>) : ListAdapter의 상태를 변경하기 위해 새로운 리스트를 설정하는 함수 

 

🤔 그렇다면 리스트의 변경 사항은 어떻게 계산할까요?

ListAdapter에서는 그 차이를 계산하기 위해서 DiffUtil.ItemCallback을 사용합니다.

DiffUtil.ItemCallback

데이터 리스트를 비교해서 차이를 계산하고 콜백 해주는 클래스

  • areItemsTheSame : 두 객체가 같은 아이템인지 비교
  • areContentsTheSame : 두 객체의 내용물이 동일한지 비교

areItemTheSame 메서드의 값이 true일 경우, 추가적으로 비교하기 위해서 areContentsTheSame을 사용합니다.

 


ListAdapter의 구현

MainActivity.kt

내부적으로는 복잡할 수 있지만 실제로 개발자가 사용하는 메서드는 간단하다는 것을 알 수 있습니다.

  • submitList(val list: List<T>) : ListAdapter의 상태를 변경하기 위해 새로운 리스트를 설정하는 함수 
class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val recyclerView = findViewById<RecyclerView>(R.id.contact_recyclerview)
        val adapter = ContactAdapter()
        recyclerView.adapter = adapter
        recyclerView.layoutManager = LinearLayoutManager(this)

        contactViewModel.contacts.observe(this) { contacts ->
            contacts.let { adapter.submitList(it) }  // 새로운 리스트 설정
        }
        ...
    }
}

→ LiveData<List>, Observable<List>를 이용하여 데이터 변경을 감지하고, 업데이트된 리스트를 ListAdapter에 제공하는 방식

 

ContactAdapter.kt

ListAdapter는 이전 리스트와 새로운 리스트를 비교하기 위해 DiffUtil.ItemCallback을 사용한다.

그 외의 구현은 일반적인 recyclerview의 adapter 구현과 동일하다!

  • areItemsTheSame : 두 객체가 같은 아이템인지 비교
  • areContentsTheSame : 두 객체의 내용물이 동일한지 비교
class ContactAdapter : ListAdapter<Contact, ContactAdapter.ContactViewHolder>(ContactComparator()) {
    ...
    class ContactComparator : DiffUtil.ItemCallback<Contact>() {
        override fun areItemsTheSame(oldItem: Contact, newItem: Contact): Boolean {
            return oldItem === newItem
        }

        override fun areContentsTheSame(oldItem: Contact, newItem: Contact): Boolean {
            return oldItem.number == newItem.number
        }

    }   
}

 

위 예시 코드에 대한 자세한 실습 예제는 아래 포스참고!

 

[Android/Jetpack] Room + LiveData : MVVM 패턴과 코루틴을 이용한 예제(2)

5. View 생성 (1) Main Activity 생성 activity_main.xml 연락처가 보여지는 화면을 그린 레이아웃 파일 contact_recyclerview : 연락처 recyclerview id add_button : 새로운 연락처 추가 버튼 id

junyoung-developer.tistory.com


reference > 

728x90