[TIL] ๐ผ24/05/10๐ผ#MutableStateFlow value vs emit vs update
MutableStateFlow value vs emit vs update
๐์ฐธ๊ณ ์๋ฃ
๐๋์๋ ๋งํ ์ง๋ ํฌ์คํ
MutableStateFlow
์ ๊ฐ์ ๋ณ๊ฒฝํ๋ ๋ฐฉ๋ฒ
- MutableStateFlow.value = newValue
- MutableStateFlow.emit(newValue)
- MutableStateFlow.update{ newValue }
value ํ๋กํผํฐ๋ก ๊ฐ ๋ณ๊ฒฝํ๊ธฐ
MutableStateFlow
์ read-write ํ๋กํผํฐ
- flow๋ฅผ subscribeํ์ง ์์๋ MutableStateFlow์ ๊ฐ set/get๊ฐ๋ฅ
- readํ๋ ๊ฒฝ์ฐ, get์ ํธ์ถํ๋ ์์ ์ snapshot ๋ฐํ
- thread-safe
emit ํจ์๋ก ๊ฐ ๋ณ๊ฒฝํ๊ธฐ
MutableStateFlow
๊ฐ์ ๋ณ๊ฒฝํ๋ suspend function
-> coroutine/coroutine context ๋ด์์ ํธ์ถํด์ผ
- flow์ ๋ฒํผ๊ฐ full์ธ ๊ฒฝ์ฐ, buffer์ ์๋ฆฌ๊ฐ ์๊ธธ ๋๊น์ง emit ํจ์ suspend ๋จ!
- ์ฐธ๊ณ )
tryEmit
ํจ์
MutableStateFlow
์ emit
์ ์๋ํ๋ ํจ์
-> emit ์ฑ๊ณต ์ true ๋ฐํ, ์คํจ ์ false ๋ฐํ
- thread-safe
update ํจ์๋ก ๊ฐ ๋ณ๊ฒฝํ๊ธฐ
MutableStateFlow
์ ๊ฐ์ atomicํ๊ฒ ๋ณ๊ฒฝํ๊ธฐ ์ํ ํ์ฅํจ์
MutableStateFlow
์ ๊ฐ์ด ์ ๋๋ก ๊ฐฑ์ ๋์๋์ง busy loop์ ๋๋ฉฐ ํ์ธ
public inline fun <T> MutableStateFlow<T>.update(function: (T) -> T) {
while (true) {
val prevValue = value
val nextValue = function(prevValue)
if (compareAndSet(prevValue, nextValue)) {
return
}
}
}
update
ํจ์๊ฐ ํ์ํ ์ด์ ?
value ํ๋กํผํฐ๋ก flow์ ๊ฐ์ ๋ณ๊ฒฝํ ๋ set ์ฐ์ฐ ์์ฒด๋ thread-safeํ์ง๋ง, ์ฃผ์ํด์ผ ํ race condition์ด ์์
viewModelScope.launch(Dispatchers.IO) {
_viewState.value = _viewState.value.copy(doneButtonEnabled = true)
}
viewModelScope.launch(Dispatchers.Default) {
_viewState.value = _viewState.value.copy(title = "New Title")
}
- ์ด๊ธฐ ์ํ
_viewState: title = "Old Title", doneButtonEnabled = false
- Launch A ์คํ ์์
_viewState: title = "Old Title", doneButtonEnabled = false
_viewState.value.copy ์ํ: title = "Old Title", doneButtonEnabled = true
- Launch A ์ผ์์ ์ง, Launch B ์คํ ์์
_viewState: title = "Old Title", doneButtonEnabled = false
_viewState.value.copy ์ํ: title = "New Title", doneButtonEnabled = false
- Launch B ์ผ์์ ์ง, Launch A ์คํ ์ฌ๊ฐ
_viewState: title = "Old Title", doneButtonEnabled = true
- Launch B ์คํ ์ฌ๊ฐ
_viewState: title = "New Title", doneButtonEnabled = false
- ์์ ๊ฐ์ ์ผ์ด ๋ฐ์ํ์ง ์์ผ๋ ค๋ฉด, copy์ value set์ amoticํ๊ฒ ์คํํด์ผ ํจ!
viewModelScope.launch(Dispatchers.IO) {
_viewState.update { it.copy(doneButtonEnabled = true) }
}
viewModelScope.launch(Dispatchers.Default) {
_viewState.update { it.copy(title = "New title") }
}
- ์ฐธ๊ณ ) mutex lock์ ์ฌ์ฉํ์ฌ atomicํ๊ฒ ์คํ๋๋๋ก ์ง์ lock์ ๊ตฌํํ๋ ๋ฐฉ๋ฒ๋ ์์
val mutex = Mutex()
viewModelScope.launch(Dispatchers.IO) {
mutex.withLock {
_viewState.value = _viewState.value.copy(doneButtonEnabled = true)
}
}
viewModelScope.launch(Dispatchers.Default) {
mutex.withLock {
_viewState.value = _viewState.value.copy(title = "New title")
}
}