일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- 면접
- 리얼하다
- 지지않는다는말
- 1일1커밋
- 커스텀린트
- 아비투스
- 브런치작가되기
- 슬기로운 온라인 게임
- 어떻게 나답게 살 것인가
- 소프시스 밤부 좌식 엑슬 테이블
- 재택근무
- 소프시스
- 테트리스
- 안드로이드
- 프래그먼트
- 좌식테이블
- 베드테이블
- 한단어의힘
- 북한살둘레길
- 자취필수템
- 함수형 프로그래밍
- 끝말잇기
- 목적 중심 리더십
- 목적중심리더십
- 한달어스
- 한달브런치북만들기
- 한달독서
- 캐치마인드
- 베드트레이
- T자형인재
- Today
- Total
정상에서 IT를 외치다
[Android] 한눈에 보는 ViewModel 초기화 방법 A to Z (feat Koin, Hilt) 본문
안녕하세요. 블랙진입니다.
이전 시간에는 안드로이드에서 제공해주는 기본 기능을 사용해 ViewModel을 초기화 하는 방법을 살펴보았습니다. 이번 시간에는 DI 라이브러리인 Koin과 Hilt을 사용한 뷰모델 초기화 방법에 대해 정리해 보겠습니다.
여기서는 각 라이브러리의 기본 사용법은 제외하고 오직 ViewModel을 초기화하는 방법에 대해서만 정리했습니다.
정리 순서는 다음과 같습니다.
1. 생성자에 파라미터가 없는 경우
2. 생성자에 파라미터가 있는 경우
3. SavedStateHandle을 사용하는 경우
🚀 SavedStateHandle에 대한 자세한 설명은 Pluu Dev님의 블로그를 참고해주세요. 여기서는 생략하겠습니다.
Koin
0. 모듈 생성
val appModule = module {
viewModel { CountViewModel() }
viewModel { CountViewModelWithParam(it[0]) }
viewModel { CountSavedStateViewModel(it[0]) }
viewModel { CountSavedStateViewModelWithParam(it[0], it[1]) }
}
Koin을 사용하기 위해서는 모듈에 ViewModel을 등록해주어야 합니다. 또한 it[n] 으로 표현된 부분은 런타임 중에 외부로 부터 직접 파라미터를 주입하겠다는 의미입니다.
1. 생성자에 파라미터가 없는 경우
예제로 사용할 ViewModel
class CountViewModel : ViewModel() {
private var count = 0
fun plusCount() {
++count
}
fun showCountLog() {
Log.d("MyTag", "count : $count")
}
}
1-1 Activity, Fragment 초기화
private val mainViewModel3 by viewModel<CountViewModel>()
Activity와 Fragment 모두 by viewModel 키워드를 사용해 초기화합니다. 참고로 Android KTX에서는 by viewModels를 사용해 초기화 해주었는데요. 뒤에 s가 붙었는가 안붙었는가의 차이가 있습니다.
1-2 SharedViewModel 초기화
//Fragment 에서 실행
private val sharedCountViewModel3 by sharedViewModel<CountViewModel>()
Koin에서는 by sharedViewModel 키워드를 사용해 초기화합니다.
2. 생성자에 파라미터가 있는 경우 (런타임시 주입)
예제로 사용할 ViewModel
class CountViewModelWithParam(
private val name: String
) : ViewModel() {
private var count = 0
fun plusCount() {
++count
}
fun showCountLog() {
Log.d("MyTag", "name : $name, count : $count")
}
}
Activity, Fragment 초기화
private val mainViewModelWithParam3 by viewModel<CountViewModelWithParam> {
parametersOf("name")
}
Activity와 Fragment 모두 by viewModel 키워드를 사용해 초기화합니다. 여기서 외부로 부터 필요한 파라미터가 있는 경우 parametersOf()를 사용해서 데이터를 주입해주면 됩니다.
3. SavedStateHandle을 사용하는 경우
3-1 생성자에 파라미터가 없는 경우 (SavedStateHandle 하나만을 파라미터로 가진 경우)
예제로 사용할 ViewModel
class CountSavedStateViewModel(
private val handle: SavedStateHandle
) : ViewModel() {
//..
}
Activity, Fragment 초기화
private val savedStateViewModel3 by stateViewModel<CountSavedStateViewModel>()
by stateViewModel 키워드를 사용해 초기화해줄 수 있습니다. 만일 위 방식이 아닌 parametersOf()를 사용해 직접 SavedStateHandle을 넘겨주고 싶은 경우 아래와 같이 작업할 수 있습니다.
private val savedStateViewModel3_1 by viewModel<CountSavedStateViewModel> {
val savedStateHandle = SavedStateViewModelFactory(application, this, null)
.create(SavedStateViewModel::class.java).savedStateHandle
parametersOf(savedStateHandle)
}
internal class SavedStateViewModel(val savedStateHandle: SavedStateHandle) : ViewModel()
SavedStateViewModelFactory 생성해서 SavedStateHandle를 가져와 파라미터로 넘기는 방법입니다. 이 경우 두개의 뷰모델이 만들어지게 되지만 동작은 동일하게 이뤄집니다.
3-2 생성자에 파라미터가 있는 경우
예제로 사용할 ViewModel
class CountSavedStateViewModelWithParam(
private val handle: SavedStateHandle,
private val name: String
) : ViewModel() {
//..
}
Activity, Fragment 초기화
private val savedStateViewModelWithParam3 by stateViewModel<CountSavedStateViewModelWithParam> {
parametersOf("name")
}
by stateViewModel 키워드를 사용할 뿐 2-1의 경우처럼 parametersOf()를 사용해서 원하는 값을 넣어주면 됩니다.
Hilt
1. 생성자에 파라미터가 없는 경우
예제로 사용할 ViewModel
class HiltViewModel @ViewModelInject constructor() : ViewModel() {
private var count = 0
fun plusCount() {
++count
}
fun showCountLog() {
Log.d("MyTag", "count : $count")
}
}
Hilt의 경우 @ViewModelInject 어노테이션을 사용해 ViewModel을 초기화해 줄 수 있습니다.
Activity, Fragment 초기화
private val mainViewModel4 by viewModels<HiltViewModel>()
Hilt는 ViewModel에 어노테이션을 추가해줄 뿐 안드로이드에서 기본으로 제공해주는 방식과 동일하게 초기화 할 수 있습니다. 위는 Android KTX를 사용해서 초기화한 방법입니다.
SharedViewModel 초기화
private val sharedCountViewModel4 by activityViewModels<HiltViewModel>()
위는 Android KTX를 사용해서 SharedViewModel 초기화한 방법으로 동일하게 동작합니다.
2. SavedStateHandle을 사용하는 경우
예제로 사용할 ViewModel
class HiltSavedStateViewModel @ViewModelInject constructor(
@Assisted private val handle: SavedStateHandle,
) : ViewModel() {
//..
}
SavedStateHandle 같은 경우에는 @Assisted 어노테이션을 통해 주입받을 수 있습니다.
Activity, Fragment 초기화
private val savedStateViewModel4 by viewModels<HiltSavedStateViewModel>()
Hilt는 ViewModel에 어노테이션을 추가해줄 뿐 안드로이드에서 기본으로 제공해주는 방식과 동일하게 초기화 할 수 있습니다. 위는 Android KTX를 사용해서 초기화한 방법입니다.
3. 생성자에 파라미터가 있는 경우 (런타임시 주입)
런타임에서 Hilt를 사용해 파라미터를 주입하는 일은 보통의 방법으로는 안됩니다. 이는 Hilt 2.31 이상 버전에서 AssistedInject를 사용해야합니다. 이에 대한 자세한 설명은 아래 링크를 통해 확인해주세요.
What's new in Hilt and Dagger 2.31
정리
ViewModel의 생성자에 파라미터가 필요할 경우 ViewModelProvider.Factory, SavedStateViewModelFactory, AbstractSavedStateViewModelFactory 등을 생성해서 초기화 해주어야 했습니다. 매번 팩토리를 생성해야 되고 필요한 파라미터를 일일히 관리해주어야 하는 번거로운 일을 Koin과 Hilt가 위와 같은 방법으로 해결해 줄 수 있습니다.
Koin과 Hilt 모두 편리하게 사용할 수 있지만 Koin의 경우가 런타임시 데이터를 유동적으로 주입해 주기가 더욱 편리한 점이 있는 것 같습니다. 하지만 Koin은 런타임시에 객체가 생성되기 때문에 의존관계가 잘못되었을 경우 문제를 사전에 찾기 힘듭니다. 이에 비해 Hilt는 컴파일 타임에서 의존관계를 모두 확인하기 때문에 문제를 사전에 찾아 해결할 수 있습니다. 하지만 런타임시에 데이터를 주입하기 위해서는 AssistedInject를 사용해야되는 약간의 번거로움이 남아 있습니다. 두 라이브러리 모두 장단점이 있다고 생각합니다.
나중에는 Koin과 Hilt 둘 중 결국 하나의 라이브러리가 우세해질까 궁금하네요?
'안드로이드' 카테고리의 다른 글
[Android] 텍스트 길이에 따른 아이콘 고정 (1) | 2021.08.03 |
---|---|
SavedStateHandle 기록용 포스팅 (0) | 2021.07.08 |
[Android] 한눈에 보는 ViewModel 초기화 방법 A to Z (1) | 2021.06.23 |
[Android, AlertDialog] 기본 다이얼로그 사용법 정리 (3) | 2021.05.07 |
프래그먼트 매니저에 대한 고찰 (0) | 2021.03.23 |