Flow의 한계
Flow는 Kotlin의 비동기적인 데이터 스트림을 처리하기 위한 라이브러리이다. 즉, Flow를 사용하면 데이터를 비동기적으로 처리하고 연속된 값을 스트림으로 표현할 수 있다.
하지만 Flow는 데이터를 저장하는 기능을 제공하지 않는다.
Flow는 데이터 스트림을 생성하고, 변환하고, 조작하는 기능을 제공하지만, 스스로 데이터를 저장할 수 없다.
따라서 Flow를 사용하여 데이터를 영구적으로 저장하고 관리해야 하는 경우, 별도의 데이터 홀더 변수를 만들어야 한다.
즉, Flow는 데이터를 저장하는 개념이 아닌 데이터의 흐름(Stream)인 것이다.
별도의 데이터 홀더 변수를 통해 데이터를 저장해보면 다음과 같은 예시를 둘 수 있다.
(메모를 작성하고 저장하는 앱을 개발한다고 가정)
Data Class
data class Memo(val id: Int, val content: String)
Room Dao
@Dao
interface MemoDao {
@Insert
suspend fun insert(memo: Memo)
@Query("SELECT * FROM memo_table")
fun getAllMemos(): Flow<List<Memo>>
}
Room Database
@Database(entities = [Memo::class], version = 1)
abstract class MemoDatabase : RoomDatabase() {
abstract fun memoDao(): MemoDao
}
ViewModel
class MemoViewModel(application: Application) : AndroidViewModel(application) {
private val database: MemoDatabase = Room.databaseBuilder(
application,
MemoDatabase::class.java,
"memo_database"
).build()
fun saveMemo(content: String) {
viewModelScope.launch {
val memo = Memo(0, content)
database.memoDao().insert(memo)
}
}
fun getAllMemos(): Flow<List<Memo>> {
return database.memoDao().getAllMemos()
}
}
getAllMemo 함수는 데이터베이스에서 모든 메모를 조회하는데 사용 된다. 이 함수는 Flow를 반환하여 데이터의 비동기적인 스트림을 생성한다.
Activity
class MemoActivity : AppCompatActivity() {
private lateinit var memoViewModel: MemoViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_memo)
memoViewModel = ViewModelProvider(this).get(MemoViewModel::class.java)
val memoAdapter = MemoAdapter()
recyclerView.layoutManager = LinearLayoutManager(this)
recyclerView.adapter = memoAdapter
memoViewModel.getAllMemos().onEach ({memos ->
memoAdapter.submitList(memos)
}.launchIn(this)
saveButton.setOnClickListener {
val content = editText.text.toString().trim()
if (content.isNotEmpty()) {
memoViewModel.saveMemo(content)
editText.text.clear()
}
}
}
}
'onEach' 함수는 Flow의 각 아이템들이 흘러나올 때마다 어떤 코드를 실행시키기 위한것으로 중간 연산자 역할을 한다.
(반면 collect 함수는 최종 연산자로 사용되어, Flow의 데이터를 소비하고 결과를 처리하는데 사용된다.)
또한, 'launchIn' 함수를 사용하여 수명 주기(lifecycle)에 따라 Flow를 실행하고 데이터를 처리한다.
Flow에서는 'collect' , 'onEach', 'launchIn'등의 함수를 사용하여 데이터를 처리하고 관찰한다.
onEach() + collect() 예제
import kotlinx.coroutines.flow.*
fun main() {
val flow = flowOf(1, 2, 3, 4, 5)
flow
.onEach { value ->
// 각 요소에 대해 동작 수행
println("Processing: $value")
}
.map { value ->
// 각 요소 변환
value * 2
}
.collect { value ->
// 처리된 요소 출력
println("Processed value: $value")
}
}
[실행결과]
Processing: 1
Processed value: 2
Processing: 2
Processed value: 4
Processing: 3
Processed value: 6
Processing: 4
Processed value: 8
Processing: 5
Processed value: 10
StateFlow의 등장
Flow는 기본적으로 데이터를 저장하는 기능을 제공하지 않는 것 이외에 다음과 같은 특징을 갖는다.
- Flow는 상태가 없다. 그러므로 현재 값을 알지 못한다.
- Flow는 Cold Stream 방식으로, 연속해서 들어오는 데이터를 처리할 수 없고, collect 할 때마다 flow가 재 실행된다.
- Flow는 안드로이드 생명주기에 대해 알지 못한다. 따라서 생명주기에 따른 처리가 필요하다.
이런 한계점들을 보완해서 나온 것이 바로 StateFlow이다.
1, 2번 보완 => StateFlow
3번 보완 => launchWhenStarted
StateFlow는 Flow 클래스를 확장한 특별한 유형의 Flow로, 상태를 저장하고 표현하고 업데이트 하는 특징을 가지고 있다.
Flow와 마찬가지로, 데이터를 저장하는 기능을 제공하지 않지만, 상태를 저장할 수 있다는 특징에서 차이가 있다.
State Flow는 다음과 같은 특징을 갖기 때문에 주로 UI 상태 관리와 같은 상태 기반의 작업에 유용하다.
Cold Stream vs Hot Stream
Cold stream
- collect() (또는 이를 subscribe 할 때)를 호출할 때마다 flow block이 재실행 된다. 즉 1~10까지 emit 하는 flow가 있다면 collect 할때마다 1~10을 전달 받는다. 여러곳에서 collect를 호출하면 각각의 collect에서 1~10을 전달받는다.
Hot stream
- collect (또는 이를 subscribe 할때)를 호출하더라도 flow block이 호출되지 않는다. collect() 시점 이후에 emit 된 데이터를 전달받는다.
StateFlow
- StateFlow는 현재 상태와 새로운 상태 업데이트를 내보내는 Observable 상태 홀더 Flow이다.
- value 속성을 통해 현재 상태 값을 읽을 수 있으며 상태를 업데이트 하고 전송하려면 MutableStateFlow 클래스의 value 속성에 새 값을 할당한다.
- StateFlow는 collector 수에 관계없이 항상 구독하고 있는 것의 최신 값을 받는다.
(값이 업데이트 된 경우에만 반환하고 동일한 값을 반환하지 않는다.) - StateFlow는 cold stream이였던 flow와 달리 hot stream이다. -> Flow는 마지막 값이 없는 반면, StateFlow는 마지막 값의 개념이 있다.
StateFlow 또한 생명주기를 알지 못하는 한계가 있기 때문에, lifeCycleScope 확장함수를 통해 생명주기에 따라 변경된 값을 collect하여 UI를 업데이트 해줄 수 있도록 한다.
StateFlow를 사용한 간단한 예제를 알아보자
ViewModel
class MyViewModel(private val repository: Repository) : ViewModel() {
private val userNameFlow = MutableStateFlow("")
val userName: StateFlow<String> = userNameFlow
init {
viewModelScope.launch {
userNameFlow.value = repository.fetchUserName()
}
}
}
StateFlow는 초기 상태를 생성자에 전달해야 한다.
Activity
class MyActivity : AppCompatActivity() {
private val viewModel = getViewModel()
override fun onCreate(savedInstanceState: Bundle?) {
lifecycleScope.launchWhenStarted { // 2.
viewModel.userName.collect { userName ->
userNameLabel.text = userName
}
}
}
}
collector를 가지고 값이 업데이트가 되는 것을 지켜보는 것을 확인할 수 있다.
StateFlow은 생명주기를 알 수 없기 때문에 상태 값을 자동으로 수집하는 것을 중지 시킬 수 없다. 따라서 lifeScope를 이용하여 생명주기를 인지 시켜주고, 그에 따라 수집을 중지 시킨다.
SharedFlow
- 이벤트 스트림: SharedFlow는 이벤트를 스트리밍하는 데 사용되며, 최근의 값이나 상태를 저장하지 않는다.. 대신에, 구독자가 구독을 시작할 때부터 새로 발생하는 이벤트를 수신한다. (StateFlow 처럼 값이 변하는 경우에만 flow 데이터를 보내는 것이 아니다!)
- 복수의 값: SharedFlow는 여러 개의 이벤트를 수집하고 방출할 수 있으며, replay 설정을 통해 몇 개의 최근 값을 재전송할 수 있다.
- 이외 디테일한 설정 값 적용 가능
- replay : 새로운 구독자들에게 이전 이벤트를 몇개 방출할지 지정 (Integer)
- extraBufferCapacity : 추가 버퍼를 몇개 생성할지 지정 (Integer)
- onBufferOverflow : 버퍼 초과시 처리 여부 (DROP_OLDEST = oldest 데이터 drop)
StateFlow vs SharedFlow
참고자료
[Coroutine Flow] 2. Flow와 StateFlow의 차이는 무엇인가?
Flow의 한계 Flow는 데이터의 흐름이다. Flow는 데이터의 흐름(flow)을 발생시키기만 할 뿐 데이터가 저장되지 않는다. 따라서 flow만을 이용해 안드로이드의 UIState를 업데이트 하기 위해서는 두가지
kotlinworld.com
[정리] — 코틀린 Flow 사용하기 (Android Dev Summit 2021)
Android Dev Summit 2021
medium.com
https://oliveyoung.tech/blog/2022-12-14/Android-State-Flow/https://readystory.tistory.com/207
[Android] LiveData VS StateFlow, 왜 StateFlow 를 써야할까?
LiveData 는 Lifecycle 라이브러리 중 하나로, 안드로이드 공통의 라이프사이클과 관련된 문제를 해결할 수 있게 해 주면서 앱 개발시 보다 더 유지보수하기 쉽게, 테스트하기 쉽게 만들어주는 라이
readystory.tistory.com
https://developer88.tistory.com/entry/StateFlow-vs-SharedFlow-%EB%A5%BC-
StateFlow vs SharedFlow 를 비교해보자 #이벤트 핸들링
오늘은 StateFlow 와 SharedFlow 에 대해서 정리해 보도록 하겠습니다. 1. 기존 글 참조 만약 SharedFlow와 StateFlow 각각에 대한 기본적인 부분들에 대해서 정리하고 싶으시다면, 아래 글들을 참조하신 다
developer88.tistory.com
'Android > Flow' 카테고리의 다른 글
Coroutine Flow(4) - 총정리 (0) | 2023.07.03 |
---|---|
Coroutine Flow(3) LiveData vs StateFlow (0) | 2023.07.03 |
Coroutine Flow(1) - Flow 란 (0) | 2023.06.27 |