오늘은 Kotlin의 StateFlow 에 대해서 정리해 보도록 하겠습니다.
StateFlow도 Flow API의 하나인데요.
Flow에 대한 내용은 아래 글을 참조해 주세요.
>> Kotlin Coroutine Flow 총정리 part3 # launchIn
1. StateFlow
StateFlow는 업데이트가 가능한 데이터값을 가지는 State라는 것을 가지고, collector에게 emit(전파)하는 인터페이스입니다.
SharedFlow를 상속받은 인터페이스 이기도 한데요.
State를 가지고 있다가, 변경되면 emit을 해주는 역할을 하는 핫스트림이라고 생각하면 됩니다.
핫스트림과 콜드스트림에 대해서는 아래 글을 참조해 주세요.
>> Kotlin Coroutine Flow 총정리 part3 # launchIn
이 StateFlow는 Complete이라는 개념 없이,
여러 Collector들에 의해서 계속해서 값이 관찰될 수 있습니다.
이 StateFlow에서 흘러나오는 아이템들을 collect하는 것은 subscriber 라고 부릅니다.
아래에서 StateFlow를 구현해보면서 이해해 보도록 하겠습니다.
2. StateFlow 구현 (안드로이드 ViewModel 에서 학생 및 admin 등록과 조회)
여기서는 안드로이드에서 학생 및 admin을 등록해서 조회하는 StateFlow를 구현하고,
Activity에서 받아서 그려보는 과정까지를 코드로 작성해 보겠습니다.
2-1. ViewModel에서 mutable 과 immutable 변수 정의
StateFlow에서는 LiveData에서처럼,
하나의 mutable(변경할 수 있는)과 immutable(변경불가능한)을 변수를 각각 설정해 주는데요.
ViewModel에서만 수정하도록 하는 private한 mutable 변수와
UI등에서 보고 반응할 수 있는 public한 immutable변수를 만들어 줍니다.
보통은 mutable변수에 대해서는 앞에 "_"(언더스코어)를 넣어주어서 구분해 줍니다.
그리고, StateFlow를 생성시에는 emptyList()가 이건, null이건, 특정 초기화값이던,
초기값을 항상 넣어주어야 합니다.
먼저 students의 변수를 설정해 줍니다.
"_"로 시작하는 mutable한 private변수를 먼저 설정해주구요.
asStateFlow()함수를 이용해서 immutable한 변수도 설정해 줍니다.
위에서는 immutable한 asStateFlow()함수를 사용해서 StateFlow()타입의 students를 생성하였는데요.
이 함수의 코드를 보면 read-only타입의 StateFlow를 만들어 준다고 나와있는데, 이러한 특성을 이용한 것 입니다.
아래에서는 admin의 변수를 정의해 주었는데요.
null로 초기화값을 설정하였습니다.
초기화를 해야한다고 해서, null이 아니어야 한다고 정하지는 않았으니까요.
2-2. Update 함수정의
이제 데이터들을 업데이트하기 위함 함수들을 viewModel에 넣어두겠습니다.
유저입력을 받고, 해당 데이터를 업데이트할 때 사용하면 되겠지요.
update함수의 코드는 아래와 같습니다.
인라인 함수이구요. MutableStateFlow.value 를 안전하게 업데이트해 줍니다.
학생을 추가하는 것 뿐만이 아니라, 기존 학생의 데이터를 업데이트해주는 함수도 필요하겠지요.
ViewModel에 학생을 삭제하는 함수도 추가해 보겠습니다.
2-3. Activity
이제 ViewModel에서 작성한 함수들을 Activity 에서 실행해 보도록 하겠습니다.
위에서 사용한 collectAsState 함수는 아래 코드에서 보듯이, 핫스트림인 StateFlow에서 최신값을 Collect해주는 함수입니다.
초기값으로 StateFlow.value 가 사용되어집니다.
실행해서 학생등록 버튼을 터치하면, 아래와 같이 동작하는 것을 볼 수 있습니다.
위에서 정의해 놓은 testStudent가 데이터로 들어와서 UI에 표현되는 것을 볼 수 있습니다.
3. stateIn
3-1. stateIn
stateIn함수는 원래 cold한 스트림인 Flow를 Hot스트림인 StateFlow로 전환시켜 주는데요.
그냥 flow가 아니라, StateFlow로 launch시켜주는 것이 StateIn()함수입니다.
리턴타입은 당연히 StateFlow의 제네릭타입입니다.
예를 들기 위해 한번 사례를 만들어보겠습니다.
만약 위에서와 같이 Stateflow를 이용해서 학생을 등록하고,
등록한 학생중에 특별한 학생을 찾으면 UI에 표시하려고 합니다.
이 특별한 학생의 id는 "s"로 시작한다고 가정해 보구요.
이 학생이 데이터에 들어오면 바로 찾고 UI에 반영하려고 하는데요.
이를 위해서 viewModel에서 아래와 같이 해 줍니다.
위에서 만든 immutable변수인 students에 map으로 찾는 것 까지는 알겠는데,
stateIn함수는 무엇일까요?
아래의 StateIn함수에서 인자로 들어간 값 들은 다음과 같은데요.
- viewModelScope: viewModelScope 에서 flow를 사용한다는 의미
- SharingStarted.WhileSubscribed: 이 변수를 subscribe하고 있을 때만 동작함을 의미
- null: 초기값을 null로 설정함을 의미
이제, student 가 업데이트 되거나 저장될 때,
specialStudent가 나오면 그것을 알게되어서 UI를 업데이트할 수 있습니다.
이런식으로, 다른 데이터를 변경할 때, 그것에 따라서 변경되는 데이터가 있다면,
StateIn을 활용해 주면 좋습니다.
3-2. combine 과 stateIn
combine 오퍼레이터를 이용하면 여러개의 아이템중 하나만 흘러나와도 바로 UI를 업데이트 할 수 있는데요.
flow를 핫스트림으로 만들어주는 stateIn과의 조합이 매우 좋습니다.
아래가 딱 맞는 예는 아니지만, 아래와 같은 식으로 combine과 stateIn 을 활용할 수 있는데요.
만약 teacher라고 하는 flow변수를 정의해 놓았다면,
students이건, teacher이건 둘중 하나의 아이템만 업데이트가 되어서 흘러나와도,
바로바로 UI에 반영할 수 있게 됩니다.
4. savedStateHandle과 같이 사용하기
savedStateHandle과 StateFlow를 같이 사용할 수도 있는데요.
코드의 주석에 보면, ViewModel에 내려오는 저장된 상태에 대한 핸들이라고 설명되어 있는데요.
쉽게 말하면, 안드로이드를 잠시 사용하다가 다른 앱으로 전환하거나 어떤 이유로 다른 화면에 갔다가 돌아왔을 때,
전환되기 전 그상태를 저장했다가 다시 받아올 수 있는 핸들러입니다.
유저를 위해 기존의 입력하거나 변화시켰던 값을 그대로 유지하려면 이것이 꼭 필요합니다.
이 객체는 아래와 같이, ViewModel의 생성자에서 받아야 한다고 나와있습니다.
이 SavedStatedHandle은 getStateFlow()라는 함수를 아래와 같이 제공해 주는데요.
무려, StateFlow를 emit해 줍니다.
아래와 같이 기존의 immutable한 변수와 mutable한 변수는 사라지고,
아래 코드 한줄로 대체할 수 있습니다.
savedStateHandle 데이터를 저장할 때도 아래와 같은 형식으로 저장하면 되므로 쉽습니다.
savedStateHandle["key이름"] = value
하지만, 간단한 변수나 상태 등을 저장할 때는 코드가 크게 간결해진다는 장점이 있는데요.
다만, 위의 student 처럼 list를 저장할 경우에는 MutableStateFlow가 가지고 있는 update함수의 도움을 받지 못해서,
오히려 코드가 길어질 수 있으니 사용할 때는 주의가 필요합니다.
5. StateFlow 구현2 (네트워크 성공 및 실패)
네트워크가 성공하거나 실패할 때의 상태에 따라서,
값을 보내주거나 Exception을 throw해주어야 하는데요.
이 때도 stateFlow를 사용할 수 있습니다.
5-1. State 정의
먼저 어떤 네트워크 상태들이 있을지를 먼저 정의해 보도록 하겠습니다.
5-2. StateFlow 변수 설정
아래와 같이 StateFlow의 mutable과 immutable 변수를 선언해 줍니다.
5-3. networkRequest 호출
이제 네트워크 리퀘스트를 호출하는 상황을 시뮬레이션하는 함수를 viewModel에 넣어줍니다.
Loading상태에 진입시킨후, 아래와 같이 3초 딜레이 후에,
Success로 간다음 1초후 다시 Ready상태로 변경합니다.
5-4. Activity 에서 구현
먼저 UI에 progress와 네트워크 호출 버튼을 아래와 같이 만들어 줍니다.
progress가 필요할 때만 보이도록 visible을 설정한 다음 아래와 같이 코드를 작성해 줍니다.
실행해 보면 다음과 같이 동작하는 것을 볼 수 있습니다.
이상으로 StateFlow에 대해서 정리해 보았습니다.
'Android 개발 > Coroutine , Flow, Channel' 카테고리의 다른 글
onEach vs onStart 비교 정리 # Kotlin Coroutine Flow (0) | 2023.05.03 |
---|---|
Coroutine suspend 동작에 관한 좋은 예와 잘못된 예 # 비동기 (0) | 2023.04.18 |
flatMapLatest 이용해서 값이 들어오는 것을 기다리기 # Coroutine (0) | 2023.04.18 |
함수 실행 시간 측정 후 Delay 사용하기 (0) | 2023.04.14 |
MutableStateFlow 이용한 로딩 후 로딩 완료 기다리기 구현 방법 (0) | 2023.04.08 |
Kotlin Coroutine Flow 총정리 part3 # launchIn (0) | 2022.10.10 |
Kotlin Coroutine 총정리 part2 # Cancellation Exception Handling (0) | 2022.10.09 |
Kotlin Coroutine 총정리 part1 # launch async Context Job CoroutineScope (2) | 2022.10.07 |
Coroutine을 이용해 Parallel한 네트워크 호출 #Kotlin (0) | 2020.03.28 |
코틀린 Coroutine으로 네트워크 Retry 구현하는 방법 (0) | 2020.03.24 |
댓글