일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- 막무가내
- 막내의막무가내 목표 및 회고
- 막내의막무가내 안드로이드
- 2022년 6월 일상
- 막내의막무가내 코볼 COBOL
- 주엽역 생활맥주
- 막내의 막무가내 알고리즘
- 프로그래머스 알고리즘
- 막내의막무가내 rxjava
- 막내의막무가내 안드로이드 에러 해결
- 부스트코스
- flutter network call
- 막내의막무가내 안드로이드 코틀린
- Fragment
- 막내의막무가내 일상
- 안드로이드
- 막내의막무가내 코틀린
- 막내의막무가내
- 막내의막무가내 코틀린 안드로이드
- 막내의막무가내 SQL
- 프래그먼트
- 막내의막무가내 플러터 flutter
- 막내의 막무가내
- 막내의막무가내 프로그래밍
- 막내의막무가내 플러터
- 막내의막무가내 알고리즘
- 부스트코스에이스
- 안드로이드 Sunflower 스터디
- 주택가 잠실새내
- 안드로이드 sunflower
- Today
- Total
막내의 막무가내 프로그래밍 & 일상
[안드로이드] 구글 공식 프로젝트 Sunflower 스터디 (6) MVVM - ViewModel 본문
[안드로이드] 구글 공식 프로젝트 Sunflower 스터디 (6) MVVM - ViewModel
막무가내막내 2021. 4. 27. 23:05
[참고]
developer.android.com/kotlin/flow/stateflow-and-sharedflow
developer.android.com/topic/libraries/architecture/coroutines?hl=ko
developer.android.com/kotlin/coroutines?hl=ko
[ViewModel]
이전 챕터에서 View에 대해 보았는데 이어서 MVVM에서 비즈니스 로직을 담당하는 ViewModel 클래스를 살펴보려고한다.
먼저 SPA(Single-Page-Application) 구조인 이 프로젝트에서 유일한 액티비티인 GardenActivity의 ViewModel 은 없다.
껍데기 역할만 함!
[PlantListViewModel]
@HiltViewModel
class PlantListViewModel @Inject internal constructor(
plantRepository: PlantRepository,
private val savedStateHandle: SavedStateHandle
) : ViewModel() {
private val growZone: MutableStateFlow<Int> = MutableStateFlow(
savedStateHandle.get(GROW_ZONE_SAVED_STATE_KEY) ?: NO_GROW_ZONE
)
val plants: LiveData<List<Plant>> = growZone.flatMapLatest { zone ->
if (zone == NO_GROW_ZONE) {
plantRepository.getPlants()
} else {
plantRepository.getPlantsWithGrowZoneNumber(zone)
}
}.asLiveData()
init {
viewModelScope.launch {
growZone.collect { newGrowZone ->
savedStateHandle.set(GROW_ZONE_SAVED_STATE_KEY, newGrowZone)
}
}
}
fun setGrowZoneNumber(num: Int) {
growZone.value = num
}
fun clearGrowZoneNumber() {
growZone.value = NO_GROW_ZONE
}
fun isFiltered() = growZone.value != NO_GROW_ZONE
companion object {
private const val NO_GROW_ZONE = -1
private const val GROW_ZONE_SAVED_STATE_KEY = "GROW_ZONE_SAVED_STATE_KEY"
}
}
PlantListFragment의 ViewModel인 PlantListViewModel 이다.
생성자로 MVVM의 Model 에 해당하는 Repository와 SaveStateHandle을 가지고 있다.
ViewModel의 데이터나 함수들은 코루틴과 연관이 많이 되어있다.
(참고로 난 코루틴을 간단하게만 학습해봤고 Flow 를 공부하는 것은 처음이다.)
데이터는 MutableStateFlow 를 사용하는데 상태를 업데이트하고 Flow에 전송하는 것이다.
추가로 일반 Flow와 비교하면 Flow는 일반적으로 cold stream이지만, StateFlow는 hot stream이다. 그러므로 일반 Flow는 마지막 값의 개념이 없고 collect 될 때만 활성화 되는 반면 StateFlow는 마지막 값의 개념이 있으며 생성하자마자 활성화된다고 한다.
처음 본 나의 느낌을 말하자면 관찰하고 스트림하고 데이터를 저장한다는 기능으로 기존의 LiveData + RxJava 같은 느낌이 들었다. (Flow는 Cold니 Subject, StateFlow는 Hot이니 Observable)
LiveData와 Flow의 여러 차이점이 있지만 그 중 하나가 위 코드 사진의 47라인 코드에서 보듯이 flatMapLatest 와 같은 Straem 함수들을 제공한다는 것이다.
그리고 viewModelScope 라는게 보이는데 개념은 다음과 같다.
ViewModelScope는 앱의 각 ViewModel을 대상으로 정의됩니다. 이 범위에서 시작된 모든 코루틴은 ViewModel이 삭제되면 자동으로 취소됩니다. 코루틴은 ViewModel이 활성 상태인 경우에만 실행해야 할 작업이 있을 때 유용합니다. 예를 들어 레이아웃의 일부 데이터를 계산한다면 작업의 범위를 ViewModel로 지정하여 ViewModel을 삭제하면 리소스를 소모하지 않도록 작업이 자동으로 취소됩니다.
- RxJava의 CompositeDisposable 와 좀 비슷하다고 느꼇다.
자세한 내용들은 다음 공식 문서 링크에서 볼 수 있다.
StateFlow, Flow, LiveData에 대한 차이점도 적혀있고 Flow 에 대한 개념설명이 잘 되어 있다. 안드로이드 공식문서(developer.android.com/kotlin/flow/stateflow-and-sharedflow)와 여러 블로그에(yoon-dailylife.tistory.com/72) Flow 기본에 대해 다음과 같이 설명이 잘 되어 있어 따로 설명은 생략하겠다.
viewModelScope는 다음 링크에서 볼 수 있다. developer.android.com/topic/libraries/architecture/coroutines?hl=ko
공식문서 짱짱!
[PlantDetailViewModel]
PlantDetailFragment의 ViewModel인 PlantDetailViewModel이다.
/**
* The ViewModel used in [PlantDetailFragment].
*/
class PlantDetailViewModel @AssistedInject constructor(
plantRepository: PlantRepository,
private val gardenPlantingRepository: GardenPlantingRepository,
@Assisted private val plantId: String
) : ViewModel() {
val isPlanted = gardenPlantingRepository.isPlanted(plantId).asLiveData()
val plant = plantRepository.getPlant(plantId).asLiveData()
fun addPlantToGarden() {
viewModelScope.launch {
gardenPlantingRepository.createGardenPlanting(plantId)
}
}
fun hasValidUnsplashKey() = (BuildConfig.UNSPLASH_ACCESS_KEY != "null")
companion object {
fun provideFactory(
assistedFactory: PlantDetailViewModelFactory,
plantId: String
): ViewModelProvider.Factory = object : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
return assistedFactory.create(plantId) as T
}
}
}
}
@AssistedFactory
interface PlantDetailViewModelFactory {
fun create(plantId: String): PlantDetailViewModel
}
@AssistedFactory나 @Assisted에 관해서는 이전의 Hilt, View 편 블로그 포스팅에서 다뤘으므로 생략하고 보면 크게 특이점은 없다.
Repository로부터 데이터를 불러오고 Flow의 확장함수인 asLiveData()를 사용하여 Flow -> LiveData로 변환시켜준다.
[GardenPlantingListViewModel]
GardenFragment의 ViewModel인 GardenPlantingListViewModel 이다.
@HiltViewModel
class GardenPlantingListViewModel @Inject internal constructor(
gardenPlantingRepository: GardenPlantingRepository
) : ViewModel() {
val plantAndGardenPlantings: LiveData<List<PlantAndGardenPlantings>> =
gardenPlantingRepository.getPlantedGardens().asLiveData()
}
이게 끝이다. 할말은 딱히 없다.
이상 Sunlfower의 ViewModel 관련 클래스들에 대해 알아봤다.
댓글과 공감은 큰 힘이 됩니다. 감사합니다. !!
'안드로이드 > 코틀린 & 아키텍처 & Recent' 카테고리의 다른 글
[안드로이드] 구글 공식 프로젝트 Sunflower 스터디 (8) Service(WorkManager) (0) | 2021.05.10 |
---|---|
[안드로이드] 구글 공식 프로젝트 Sunflower 스터디 (7) MVVM - Model (5) | 2021.05.09 |
[안드로이드] 구글 공식 프로젝트 Sunflower 스터디 (5) MVVM - View (0) | 2021.04.19 |
[안드로이드] 구글 공식 프로젝트 Sunflower 스터디 (4) Hilt Dependency Injection (10) | 2021.04.19 |
[안드로이드] 구글 공식 프로젝트 Sunflower 스터디 (3) Jetpack Navigation 구조 (0) | 2021.04.19 |