[TIL] ๐ŸŒผ24/04/30๐ŸŒผ#StateFlow #SharedFlow

0

TIL

๋ชฉ๋ก ๋ณด๊ธฐ
92/100
post-thumbnail

[TIL] ๐ŸŒผ24/04/30๐ŸŒผ#StateFlow #SharedFlow

StateFlow, SharedFlow

๐Ÿ“Œ์ฐธ๊ณ ์ž๋ฃŒ

๐Ÿ“Œ์ฐธ๊ณ ํ•˜๋ฉด ์ข‹์„ ์ง€๋‚œ ํฌ์ŠคํŒ…

  • StateFlow, SharedFlow:
    Flow APIs that enable flows to optimally emit state updates and emit values to multiple consumers

StateFlow

  • state-holder observable
  • requires initial state to construct
  • emits current value & new state updates to its collectors
  • unlike Flow, StateFlow is hot
    -> collecting from the flow doesn't trigger any producer code
  • always active in memory (๋ชจ๋“  reference๊ฐ€ ์—†์–ด์ง€๋ฉด GC ๋Œ€์ƒ์ด ๋จ)

SharedFlow

  • SharedFlow is hot, like StateFlow
  • emits values to all comsumers that collect from it

LiveData์—์„œ Flow๋กœ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜

๐Ÿ“Œ์ฐธ๊ณ ์ž๋ฃŒ

val myStateFlow = myFlow.stateIn(
	scope = lifecycleScope,
	started = SharingStarted.Lazily,
	initialValue = ์ดˆ๊ธฐ๊ฐ’
)

๐Ÿ“Œ์ฐธ๊ณ ํ•˜๋ฉด ์ข‹์„ ์ง€๋‚œ ํฌ์ŠคํŒ…

LiveData -> StateFlow

  • LiveData์—์„œ StateFlow๋กœ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ํ•  ๋•Œ ๊ฐ€์žฅ directํ•œ ๋ฐฉ๋ฒ•:
    MutableLiveData๋ฅผ MutableStateFlow๋กœ ๋ฐ”๊พธ๊ธฐ!
  • MutableLiveData์˜ ํŠน์ง•:
    • part of AAC(Android Architecture Components)
    • Default๋กœ, new value์™€ old value๊ฐ€ distinctํ•œ์ง€ ์ฒดํฌํ•˜์ง€ X
      -> new value์™€ old value๊ฐ€ ๊ฐ™๋”๋ผ๋„, observer์— update notification ์ „๋‹ฌ
    • main thread์—์„œ ์ˆ˜ํ–‰๋จ
  • MutableStateFlow์˜ ํŠน์ง•:
    • part of Kotlin's coroutines library
    • value์— distinct change๊ฐ€ ๋ฐœ์ƒํ•œ ๊ฒฝ์šฐ์—๋งŒ update ํŠธ๋ฆฌ๊ฑฐ ํ•จ
      (distinctํ•œ์ง€ ์•„๋‹Œ์ง€๋Š” equality check(==)๋กœ ํŒ๋ณ„)
    • integrated with Kotlin's coroutine framework -> ๋น„๋™๊ธฐ ์ž‘์—… ์ˆ˜ํ–‰์— ํŠนํ™”
      • value๋ฅผ setํ•  ๋•Œ coroutine context ๋‚ด์—์„œ ์ˆ˜ํ–‰๋จ
      • updates are queued and delivered in an orderly & non-blockable manner
        -> high-frequency data update ์ฒ˜๋ฆฌ์— ์œ ๋ฆฌ
  • MutableLiveData์™€ MutableStateFlow ์‚ฌ์šฉ syntax
    • ๋‘˜๋‹ค value ํ”„๋กœํผํ‹ฐ๋กœ value get & set
  • observingMutableLiveData
    • Lifecycle Awareness (Activity/Fragment์˜ ์ƒ๋ช…์ฃผ๊ธฐ์— ๋ฌถ์ž„)
    • UI-์ค‘์‹ฌ์ 
      // Observing LiveData in an Activity or Fragment
      personNameLiveData.observe(this, Observer { name ->
         // Update UI with the new name
      })
  • collecting MutableStateFlow
    • No inherit lifecycle awareness
      -> ๊ฐœ๋ฐœ์ž๊ฐ€ ์ƒ๋ช…์ฃผ๊ธฐ ๊ด€๋ฆฌํ•ด์•ผ
      (ex. lifecycleScope ์ง€์ •, Lifecycle.repeatOnLifecycle ๋ธ”๋ก ์‚ฌ์šฉ)
    • UI & UI ๊ด€๋ จ data stream ๊ด€๋ฆฌ์— ์ ํ•ฉ
    • data stream ๋ณ„๋กœ ๋ณ„๋„์˜ coroutine launch ํ•„์š”
      // Collecting StateFlow in a Coroutine
      lifecycleScope.launch {
         personNameStateFlow.collect { name ->
             // Update UI with the new name
         }
      }

MediatorLiveData -> Flow

  • MediatorLiveData ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ๋ฐฉ๋ฒ•:
    ์—ฌ๋Ÿฌ MutableStateFlow๋ฅผ combine/zipํ•˜์—ฌ Flow๋กœ ํ•ฉ์น˜๊ธฐ!

  • ImageSearch ํ”„๋กœ์ ํŠธ SearchViewModel.kt ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ์ „

     private val _searchItems = MutableLiveData<List<Item>>(emptyList())
      val searchItems: LiveData<List<Item>> get() = _searchItems
      
      private val _savedItems = MutableLiveData<List<Item>>(emptyList())
      val savedItems: LiveData<List<Item>> get() = _savedItems
    
      private val _resultItems = MediatorLiveData<List<Item>>().apply {
          val onChange = { _: List<Item> ->
              value = searchItems.value!!.map { item ->
                  savedItems.value!!.find { it.id == item.id }?.let {
                      item.copy(folderId = it.folderId)
                  } ?: item.copy(folderId = FolderId.NO_FOLDER.id)
              }
          }
          addSource(searchItems, onChange)
          addSource(savedItems, onChange)
      }
      val resultItems: LiveData<List<Item>> get() = _resultItems
  • ImageSearch ํ”„๋กœ์ ํŠธ SearchViewModel.kt ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ํ›„

     private val _searchItems = MutableStateFlow<List<Item>>(emptyList())
      private val searchItems: StateFlow<List<Item>> get() = _searchItems
    
      private val _savedItems = MutableStateFlow<List<Item>>(emptyList())
      val savedItems: StateFlow<List<Item>> get() = _savedItems
    
      private val _resultItems =
          searchItems.combine(savedItems) { searchItems, savedItems ->
              searchItems.map { item ->
                  savedItems.find { it.id == item.id }?.let {
                      item.copy(folderId = it.folderId)
                  } ?: item.copy(folderId = FolderId.NO_FOLDER.id)
              }
          }
      val resultItems: Flow<List<Item>> get() = _resultItems

0๊ฐœ์˜ ๋Œ“๊ธ€