-
๋ฐ์ํ
๐ LiveData์์ StateFlow(Flow)๋ก ์ด์ ํ ์ด์ ?
ํ๋ ์น ํ๋ก์ ํธ๋ domain layer๊ฐ ์๋ ์๋๋ก์ด๋ ์ฑ ์ํคํ ์ฒ ๊ฐ์ด๋๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ๊ฐ๋ฐํ์๋ค.
๊ฑฐ์ ๋ชจ๋ ๋๋ฉ์ธ ๋ก์ง์ด ๋ฐฑ์๋ ์๋ฒ์ ์์ด์ ํด๋ฆฐ ์ํคํ ์ฒ๋ก ๊ตฌํํ ์ด์ ๊ฐ ์์๊ณ
๋น ๋ฅธ ์๋๋ก ๊ฐ๋ฐํ๊ณ ์ถ์ํด์ผํ๊ธฐ ๋๋ฌธ์ domain layer๋ฅผ ํฌํจํ์ง ์์๋ค.
ํ์ง๋ง Marvel ํ๋ก์ ํธ๋ฅผ ํด๋ฆฐ์ํคํ ์ฒ(domain layer๊ฐ ์๋)๊ธฐ๋ฐ์ผ๋ก ์งํํ์๋ค.
Domain Layer๋ ๋ค๋ฅธ ํ๋ ์์ํฌ์์๋ ์ฌ์ฉํ ์ ์๋๋ก Android์ ์์กด์ฑ์ ๊ฐ์ง์ง ์์ ์์ Java/Kotlin ์ฝ๋๋ก ๊ตฌ์ฑํด์ผํ๋๋ฐ LiveData๋ Android ํ๋ซํผ์ ์ข ์์ ์ด๋ค.
๊ทธ๋์ Domain Layer์ Android ์ ์์กด์ฑ์ด ์๋ Flow๋ฅผ ๋์ ํ์๊ณ ,
ViewModel์์๋ LiveData๋ฅผ ๋์ฒดํ์ฌ StateFlow๋ฅผ ์ฑํํ์๋ค.
๐ฆ LiveData vs StateFlow
LiveData์ StateFlow๋ ๋น์ทํ์ ์ด ๋ง๋ค.
๋๋ค ๊ด์ฐฐ ๊ฐ๋ฅํ data holder class์ด๊ณ , ์ฑ ์ํคํ ์ฒ์ ์ฌ์ฉํ ๋ ๋น์ทํ ํจํด์ ๋ฐ๋ฅธ๋ค.
ํ์ง๋ง ์ด ๋์ ๋ค๋ฅด๊ฒ ์๋ํ๋ค.
LiveData StateFlow ๊ด์ฐฐ ๊ฐ๋ฅํ data holder class ์ฑ ์ํคํ ์ฒ์ ์ฌ์ฉํ ๋ ๋น์ทํ ํจํด์ ๋ฐ๋ฆ Android ํ๋ซํผ์ ์ข ์์ ์์ kotlin ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์๋ช ์ฃผ๊ธฐ๋ฅผ ๊ฐ์ง Activity, Fragment ๋ฑ์ ๊ด์ฐฐํ๊ณ , ๊ด์ฐฐ ๋์์ ์๋ช ์ฃผ๊ธฐ์ ๋ฐ๋ผ ๋ฐ์ดํฐ๋ฅผ ์ ๋ฌํ๋ค.
(๋ทฐ๊ฐ STOPPED ์ํ๊ฐ ๋๋ฉด
LiveData.observe()๋ ์๋น์๋ฅผ ์๋์ผ๋ก ๋ฑ๋ก ์ทจ์)์๋๋ก์ด๋ ์๋ช ์ฃผ๊ธฐ๋ฅผ ๋ชจ๋ฅธ๋ค.
(๋ทฐ๊ฐ STOPPED ์ํ๊ฐ ๋์ด๋
์๋์ผ๋ก ์์ง์ ์ค์งํ์ง ์์)
๋์ผํ ๋์์ ํ๋ ค๋ฉด Lifecycle.repeatOnLifecycle ๋ธ๋ก์์ ํ๋ฆ์ ์์งํด์ผํ๋ค* Guide *
Android ์๋ช ์ฃผ๊ธฐ์์ ํตํฉ์ด ์ค์ํ ๊ฒฝ์ฐ --> LiveData
Flow ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํ๊ณ ์ถ์ ๊ฒฝ์ฐ --> StateFlow
์ ๋ฆฌ : Clean Architecture ๊ด์ ์์ LiveData๋ฅผ ๋์ฒดํ์ฌ Flow๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ด ์ข๋ค.
๊ทธ๋ฌ๋ฉด StateFlow๋ฅผ ์ฌ์ฉํ๋ ์์๋ฅผ ๋ณด์
ComicsViewModel.kt
@HiltViewModel class ComicsViewModel @Inject constructor( private val getComicsUseCase: GetComicsUseCase ): ViewModel(){ private val _comicsListFlow = MutableStateFlow<List<Comic>>(emptyList()) val comicsListFlow: StateFlow<List<Comic>> = _comicsListFlow fun getComics(){ viewModelScope.launch{ getComicsUseCase().collect { _comicsListFlow.value = it } } } }
ComicsFragment.kt
@AndroidEntryPoint class ComicsFragment : Fragment() { private lateinit var binding : FragmentComicsBinding private val comicsViewModel: ComicsViewModel by viewModels() override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { binding = FragmentComicsBinding.inflate(layoutInflater) val adapter = ComicsAdapter() binding.comicRecyclerview.adapter = adapter subscribeUi(adapter) return binding.root } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) comicsViewModel.getComics() } private fun subscribeUi(adapter: ComicsAdapter) { lifecycleScope.launch { repeatOnLifecycle(Lifecycle.State.STARTED) { comicsViewModel.comicsListFlow.collect{ list -> adapter.submitList(list) } } } } }
StateFlow ์ ๋ฆฌ
- ํ์ฌ ์ํ์ ์๋ก์ด ์ํ ์ ๋ฐ์ดํธ๋ฅผ ์์ง๊ธฐ์ ๋ด๋ณด๋ด๋ ๊ด์ฐฐ ๊ฐ๋ฅํ ์ํ ํ๋ Flow
- value ๋ฅผ ํตํด ํ์ฌ ์ํ ๊ฐ์ ์ฝ์ ์ ์์
- ์ํ๋ฅผ ์ ๋ฐ์ดํธํ๊ณ ํ๋ฆ์ ์ ์กํ๋ ค๋ฉด MutableStateFlow ํด๋์ค์ value ์ ์ ๊ฐ ํ ๋น
- hot stream : StateFlow์์ ์์ง๋๋ ๋ชจ๋ ํด๋์ค๊ฐ ์๋น์ ( flow ๋ cold stream : ํ๋์ ์๋น์์๊ฒ ์ ์ก)
- LiveData์ ๋ค๋ฅด๊ฒ ์๋ช ์ฃผ๊ธฐ๊ฐ Destroy ๋ ๋ ๊น์ง ๋ฐ์ดํฐ๋ฅผ ์์งํ๋ค. ๊ทธ๋์ ๋ทฐ๊ฐ ํ์๋์ง ์์ ๋ ๋๋ ์ฑ์ด ๋ฐฑ๊ทธ๋ผ์ด๋์ ์์ ๋๋ ๋ฐ์ดํฐ๋ฅผ ์์งํ์ฌ ๋ฉ๋ชจ๋ฆฌ ๋ฌธ์ ๋ฅผ ๋ฐ์์์ผ ์ฑ์ด ๋ค์ด๋ ์ ์๋ค. ์ด๋ฅผ ๋ฐฉ์งํ๊ธฐ ์ํด์๋ repeatOnLifecycle API๋ฅผ ์ฌ์ฉํ์ฌ์ผํ๋ค. ํ์ง๋ง! ๋ฐฑ๊ทธ๋ผ์ด๋์์๋ ์ํ๋์ด์ผ ํ๋ ์์ ์ repeatOnLifecycle์ ์ฌ์ฉํ๋ฉด ์๋๋ค.
https://github.com/youlalala/Marvel
reference)
https://developer.android.com/kotlin/flow/stateflow-and-sharedflow?hl=ko#livedata
๋ฐ์ํ'AOS' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[Android] Github Release ์๋ํ (Github Actions) (2) 2023.11.19 [Android] Clean Architecture ๊ธฐ๋ฐ์ Multi Module ๋์ ํ๊ธฐ (1) 2023.11.15 [Andorid] local.properties๋ฅผ ์ด์ฉํ์ฌ API URL, KEY ์์ ํ๊ฒ ์จ๊ธฐ๊ธฐ (0) 2023.06.13 [Android] ๋ฌดํ์คํฌ๋กค Infinite Scroll (0) 2023.05.17 [Android] ์ด๋ฏธ์ง ๋ผ์ด๋ธ๋ฌ๋ฆฌ(glide)์์ด URL๋ก ์ด๋ฏธ์ง ๋ํ๋ด๊ธฐ (0) 2023.05.12