일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- 암호학
- TEST
- Kotlin
- Coroutine
- activity
- 코드포스
- boj
- textfield
- 백준
- architecture
- MyVoca
- relay
- Rxjava
- android
- 쿠링
- Compose
- Gradle
- Coroutines
- Codeforces
- 코루틴
- Python
- AWS
- ProGuard
- MiTweet
- androidStudio
- Hilt
- pandas
- GitHub
- livedata
- 프로그래머스
- Today
- Total
이동식 저장소
LiveData, Room 그리고 ListAdapter 본문
ListAdapter
Android에서 여러 개의 항목을 보여줘야 할 때는 거의 ``RecyclerView``를 사용한다. ``RecyclerView``에 데이터를 제공하는 방법은 여러 가지가 있는데, 최근에 ``ListAdapter``를 알게 되어 MyVoca에 적용해 보았다.
참고: DiffUtil and data binding with RecyclerView - Codelabs for Android Kotlin Fundamentals (Google)
기존 ``RecyclerView.Adapter``는 리스트가 변경될 때마다 ``notify...`` 메소드를 실행해줘야 값이 제대로 보인다. 그런데 데이터의 삽입, 삭제, 수정 등 상황마다 실행해야 하는 메소드가 달라서 (솔직히) 귀찮다. 그래서 궁극의 ``notifyDataSetChanged()``로 퉁치는 경우도 있지만, 이렇게 하면 전체 데이터를 전부 다시 그리기 때문에 성능상 좋지 않다.
``ListAdapter``는 리스트의 변경사항을 자동으로 추적하여 내부적으로 ``notify...`` 메소드를 실행한다. 우리는 두 데이터 객체의 동일성과 동등성을 판정하는 클래스만 작성하면 된다. 자세한 내용은 위의 링크를 참고하자.
그런데
난 왜 갱신이 안 되지? 뭔가 버그가 있다. 말로 설명하기 어려우니 직접 한번 보자.
``RecyclerView``에서 아이템을 왼쪽으로 스와이프하면 아이템이 삭제되도록 하는 기능을 구현하였다. 그런데``RecyclerView``가 제대로 갱신되지 않는다. 단어가 제대로 삭제된 것 같지도 않다. 단어를 하나 지웠음에도 단어 수가 그대로 4개이다. 심지어 취소 버튼을 누르면 앱이 죽네?
절대로 사소한 문제가 아니다! 데이터 누수가 생길 수도 있기 때문이다.
원인: 리팩토링 실수
beta-1.12.0에서 코루틴을 도입하면서 코드를 대대적으로 리팩토링하였다. 그런데 ``ViewModel`` 코드를 리팩토링하는 과정에서 실수가 있었다.
// ViewModel // RecyclerView에 보여야 하는 단어 private val _currentVocabulary = MutableLiveData<MutableList<RoomVocabulary?>?>() val currentVocabulary: LiveData<MutableList<RoomVocabulary?>?> get() = _currentVocabulary ... // 단어 삭제 메소드 fun deleteItem(position: Int) = viewModelScope.launch(Dispatchers.IO) { val target = currentVocabulary.value?.get(position) ?: return@launch _currentVocabulary.value?.removeAt(position) // 1 vocaRepository.deleteVocabulary(target.toVocabulary()) // 2 }
1번 줄은 리스트에서 단어를 지우고, 2번 줄은 데이터베이스에서 단어를 지운다. 그런데 1번과 2번 모두 ``LiveData``의 값을 바꾸었으므로 각각 observer를 호출하게 된다.
// observer viewModel.currentVocabulary.observe(viewLifecycleOwner) { it -> it?.let { vocaRecyclerViewAdapter?.submitList(it) } // 3. 리스트 갱신 vocaRecyclerViewAdapter?.notifyDataSetChanged() // 4 }
4번 줄에서 ``notify`` 메소드를 실행하고 있다. 3번에서 알아서 실행될 텐데! 이러면 데이터 누수를 막을 수 있지만, 그래픽이 매우 뚝뚝 끊기는 문제가 발생한다. 사실 이건 beta-1.12.0에서 코루틴을 도입할 때부터 있었던 이슈라서 꼭 고치고 싶기도 했다.
해결: DB에 맡겨라
코루틴을 도입하여 얻은 성과 중 하나는 내부 DB의 변경사항이 ``currentVocabulary``에 그대로 반영된다는 점이다. 그러니까 단어를 ``vocaRepository``에서만 지우면 ``currentVocabulary``에도 그대로 반영된다는 뜻이다.
즉 1번 줄을 없애야 한다. 4번 줄은 1번 줄이 없었더라면 애초에 필요없는 코드이므로 지워도 된다. 아니 지워야 한다. 그래야 UI가 더 부드럽게 작동한다.
해결!
해묵은 버그를 수정하여 기쁜 마음에 글을 작성해 보았다.
'프로젝트 > MyVoca' 카테고리의 다른 글
MyVoca 1.13.0 출시 (0) | 2021.06.13 |
---|---|
MyVoca 1.12.4 출시 (0) | 2021.05.26 |
MyVoca 1.12.3 출시 (0) | 2021.05.10 |
Bitrise로 Continuous Delivery 제공하기 (0) | 2021.04.20 |
Google Play에 MyVoca 출시 (0) | 2021.03.18 |