Skip to content

Controller 와 ViewHolder 간의 의존성 제거 해보기

Sieun Ju edited this page Jan 16, 2022 · 3 revisions

요약 ( Summary)

  • ViewModel 과 RecyclerView.ViewHolder 간의 의존성을 최대한 줄여서 보다 편한 유지보수를 합니다.

배경 (Background)

  • 안드로이드 프레임워크에서 RecyclerView를 아주 자주 사용하게 되는데 이때 아이템 클릭에 대한 처리는 ViewModel 에서 하거나 ViewHolder 자체내에서 가능한 경우라면 자체내에서 처리합니다. ex.) 상품 클릭 → ViewHolder 자체내에서 startActivity로 풀어낼수가 있습니다.
  • 하지만, 네트워크 통신이 필요로 하는 경우라면 ViewModel 에서 처리를 해야 합니다. 이유는 Android Hilt 에서 DI 는 기본적으로 ViewModel, Activity...등등만 지원하기 때문입니다.
  • 이러한 이유로 운영 / 유지보수 단계에서 페이지를 만들고 ViewHolder 를 만들었을때 ViewModel 간의 의존관계가 높아서 유지보수하기가 어렵다는 단점이 있습니다.

학습한 내용 정리

  • Hilt의 Custom EntryPoint 를 활용하여 ViewHolder 에서도 모듈화된 인터페이스를 사용합니다.
package com.hmju.presentation.simple_like_recyclerview

import com.hmju.domain.repository.GoodsRepository
import dagger.hilt.EntryPoint
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent

@EntryPoint
@InstallIn(SingletonComponent::class)
interface SimpleLikeEntryPoint {
    fun goodsRepository(): GoodsRepository
}
package com.hmju.presentation.simple_like_recyclerview

BaseSimpleLikeViewHolder.kt

private val simpleLikeEntryPoint: SimpleLikeEntryPoint by lazy {
	EntryPoints.get(itemView.context.applicationContext, SimpleLikeEntryPoint::class.java)
}
  • RxBusEvent 를 활용하여 Ui 업데이트를 진행합니다.
package com.hmju.presentation.simple_like_recyclerview

BaseSimpleLikeViewHolder.kt

// 좋아요 이벤트 발생시 Ui Update
RxBus.listen(RxBusEvent.SimpleLikeEvent::class.java)
	.observeOn(AndroidSchedulers.mainThread())
  .subscribe({
		// Ui Update
	  onRefreshLike()
  }, {})
  • RecyclerView.ViewHolder 의 생명주기를 활용하여 네트워크 통신시 ‘메모리 누수’ 이슈를 해결합니다.
    • RecyclerView.ViewHolder 에서 Lifecycle을 가져오는 방법은 여러가지가 있는데 그중 Lifecycle에서 지원하는 findViewTreeLifecycleOwner 함수를 통해 가져옵니다. (v 2.3.1 에서는 지원함)
    • RecyclerView.Adapter 오버라이딩 함수인 onViewAttachedToWindow or onViewDetachedFromWindow 에서 처리하지 않고 doOnAttach or doOnDetach 를 통하여 메모리 누수 나 addObserver 처리를 했습니다. 그 이유는 RecyclerView 에서 뷰들을 직접적으로 Add 하는 영역은 attachView 이고 또한 뷰들을 재사용하다가 제거하는 부분은 doOnDetach 이기 때문에 매번 onViewAttachedToWindow or onViewDetachedFromWindow 에서 RxBusEvent 를 초기화 하거나 해제를 하지 않고 RecyclerView에서 실제로 ‘추가’ 하거나 ‘제거’ 할때 처리하도록 비용 문제를 해결했습니다.
package com.hmju.presentation.simple_like_recyclerview

BaseSimpleLikeViewHolder.kt

doOnDetach {
	// Remove Observer
	// Diposable 해제 처리
}

// Activity or Fragment Lifecycle 이 onStop 인경우
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
private fun onStop() {
	// Disposable 해제 처리
}

테스트 (Test)

  • 한 페이지에 같은 API 를 호출하는 RecyclerView 를 2개 만들어서 좋아요 선택시 실시간으로 UI 가 업데이트 되는지 확인
  • 좋아요 API POST /api/like 요청할때 Delay 3초로 두고 나서 해당 페이지를 닫을때 메모리 누수가 발생하는지 확인
  • 같은 아이템을 가진 리스트 페이지를 여러개 만들고 여러개 좋아요 선택후 페이지를 하나씩 닫을때 좋아요 아이템 동기화가 제대로 이루어지는지 확인
  • 페이지가 onStop 이였다가 다시 onResume 상태로 왔을때 현재 보여지고 있는 View 만 동기화 이루어지는지 확인
  • ViewPager 로 여러개의 Fragment 로 나누고 스와이프 할때마다 아이템 동기화 처리 되는지 확인

회고의 시간 (ㅠㅠ)

  • Repository 패턴으로 API 를 호출했는데 UseCase 패턴으로도 될거 같은데 그방안은 찾아본후에 업데이트 해보겠습니다.
  • 기존에 Hilt 로 DI 와 모듈화를 구성할때 기본적인 ‘기능’ 만 사용했는데 요번에 EntryPoint 에 대해서 공부한 계기가 되었습니다.
  • RecyclerView 를 나름? 잘 사용한다고 생각 했는데 역시 배움의 끝은 없다고 느꼈습니다.

참고 (Reference)

Clone this wiki locally