본문 바로가기
안드로이드(kotlin)

MVVM에서 viewModel 이벤트를 받을 수 있는 방법-2

by 기계공학 주인장 2023. 3. 30.
반응형
 

MVVM에서 viewModel 이벤트를 받을 수 있는 방법-1

MVVM에서 viewModel 이벤트를 받을 수 있는 방법 LiveData만 사용해서 이벤트 처리하기 LivieData에 EventFlow를 래핑해서 처리하기 SingleLiveData로 이벤트 처리하기 StateFlow, SharedFlow로 이벤트 처리하기 SharedFl

android-developer.tistory.com

 

MVVM에서 viewModel 이벤트를 받을 수 있는 방법

  1. SingleLiveData로 이벤트 처리하기
  2. StateFlow, SharedFlow로 이벤트 처리하기
  3. SharedFlow, Sealed class로 이벤트 처리하기
  4. SharedFlow & Sealed class & LifeCycle로 이벤트 처리하기
  5. EventFlow & Sealed class Lifecyle로 이벤트 처리하기

SingleLiveData를 사용해서 데이터 이벤트 받기

기존의 방법인 EventWrapper를 사용하여 viewModel 이벤트를 받는 방법은

 

매번 liveData 변수를 Wrapping하고 데이터 입출력도 Wrapping을 해야 하기 때문에 상당히 번거로웠다.

 

그래서 SingleLiveData라는 것을 사용하여 이미 Wrapping된 viewModel을 가져오는 방식이 사용되었다.

 

SingleLiveData 정의하기

기존에 EventWrapper를 사용하는 것과 동일하다.

 

abstract class SingleLiveData<T> {
	
    // Event로 래핑
    private val liveData = MutableLiveData<Event<T>>()

    protected constructor()

    protected constructor(value: T) {
        liveData.value = Event(value)
    }

    protected open fun setValue(value: T) {
        liveData.value = Event(value)
    }

    protected open fun postValue(value: T) {
        liveData.postValue(Event(value))
    }

    // 기존에 저장되어있는 값을 가져온다 = 기존과 동일한 방법
    fun getValue() = liveData.value?.peekContent()

    // 항상 첫번째로 들어간 값만 가져온다
    fun getNotHandledValue() = liveData.value?.getContentIfNotHandled()

    // 기존에 저장되어있는 값을 관찰한다. = 기존과 동일한 방법
    fun observePeek(owner: LifecycleOwner, onResult: (T) -> Unit) {
        liveData.observe(owner) { onResult(it.peekContent()) }
    }

    // 항상 첫 번째로 들어간 값만 관찰한다.
    fun observe(owner: LifecycleOwner, onResult: (T) -> Unit) {
        liveData.observe(owner) { it.getContentIfNotHandled()?.let(onResult) }
    }
}

 

MutableSingleLiveData 정의하기

SingleLiveData를 사용하기 위한 MutableSingleLiveData를 정의한다.

 

class MutableSingleLiveData<T> : SingleLiveData<T> {

    constructor() : super()

    constructor(value: T) : super(value)

    public override fun postValue(value: T) {
        super.postValue(value)
    }

    public override fun setValue(value: T) {
        super.setValue(value)
    }
}

 

SingleViewModel 정의하기

viewModel 또한 지금까지 정의했던 것과 동일한 방법으로 정의한다.

 

class SingleViewModel : ViewModel() {
    private val _someData = MutableSingleLiveData<Int>()
    val someData: SingleLiveData<Int> = _someData

    fun setData() {
        if (_someData.getNotHandledValue() == null) {
            _someData.setValue(0)
        } else {
            _someData.setValue(_someData.getNotHandledValue()!! + 1)
        }
    }
}

 

이렇게 EventWrapper와 SingleLiveData를 사용하여 

 

매 번 초기화된 liveData를 받을지

 

계속 데이터가 유지되는 liveData를 받을지 선택할 수 있습니다.


SharedFlow를 사용해서 viewModel Event 수신

하지만 liveData만 사용하다 보니 클린 아키텍처를 사용할 때 발생하는 문제가 있습니다.

 

클린 아키텍처에선 각 Layer는 나뉘어 있고

 

viewModel은 Presenters Layer에 위치하고 있기 때문에

 

특정 플랫폼과 관계가 없어야 합니다.

 

즉, import android.xxx 인 코드가 없도록 유지되어야 한다는 뜻입니다.

 

해당 글은 구글 직원이 쓴 블로그에도 나와 있는 내용입니다.

 

Don’t let ViewModels (and Presenters) know about Android framework classe
 

ViewModels and LiveData: Patterns + AntiPatterns

A collection of patterns and recommendations that we’ve been collecting since we released the first alpha version of the Architecture Components.

medium.com

 

하지만 지금까지 사용했던 LiveData는 안드로이드 패키지에 해당하는 기술입니다.

 

import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel

 

하지만 sharedFlow를 사용한다면 해당 패키지를 없앨 수 있습니다.

 

다음과 같이 sharedFlowViewModel을 구현합니다.

 

import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
// androidx가 kotlinx 패키지로 교체됨
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.launch

class SharedFlowViewModel : ViewModel() {
    private val _someData = MutableSharedFlow<Int>()
    val someData = _someData.asSharedFlow()

    fun setData() {
        viewModelScope.launch {
            _someData.emit(1)
        }
    }
}

 

이렇게 함으로써 안드로이드 패키지에 의존하지 않고 viewModel을 구성할 수 있게 되었습니다.

 

그다음 Acitivity or Fragment에서 다음과 같이 정의합니다.

 

lifecycleScope.launch {
    sharedFlowViewModel.someData.collect {
        Timber.d("sharedViewModel data observed: $it")
        openSecondActivity()
    }
}

binding.sharedFlowButton.setOnSingleClickListener {
    sharedFlowViewModel.setData()
}

 

 

sharedFlow의 경우 get이나 value로 값을 직접 가져올 수 없고

 

collect를 통해서 emit 되는 값만 받아올 수 있습니다.

 

직접 값을 가져와야 할 필요가 있다면 stateFlow를 사용하면 됩니다


위 블로그는 아래 블로그를 참조하여 만들었습니다.

 

MVVM의 ViewModel에서 이벤트를 처리하는 방법 6가지

지금 개발하시는 코드에서 ViewModel의 이벤트 처리를 어떻게 하고 계신가요? 헤이딜러에서 LiveData -> SingleLiveData -> SharedFlow -> EventFlow로 이벤트 처리 방법을 변화 하기까지 과정을 소개합니다…

medium.com


 

 

MVVM에서 viewModel 이벤트를 받을 수 있는 방법-3

MVVM에서 viewModel 이벤트를 받을 수 있는 방법 SharedFlow, Sealed class로 이벤트 처리하기 SharedFlow & Sealed class & LifeCycle로 이벤트 처리하기 EventFlow & Sealed class Lifecyle로 이벤트 처리하기 이전 블로그에서

android-developer.tistory.com

 

반응형


"이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다."


댓글