MVVM-1에서 viewModel 이벤트를 수신하는 방법

MVVM에서 viewModel 이벤트를 수신하는 방법

  1. LiveData만 있는 이벤트 처리
  2. EventFlow를 LivieData에 래핑하여 처리
  3. SingleLiveData로 이벤트 처리
  4. StateFlow 및 SharedFlow로 이벤트 처리
  5. SharedFlow, Sealed 클래스로 이벤트 처리
  6. SharedFlow & Sealed Class & LifeCycle로 이벤트 처리
  7. EventFlow 및 Sealed 클래스 수명 주기로 이벤트 처리

프로젝트 설정

프로젝트 설정 방법을 간략하게 설명하겠습니다.

다음과 같이 사용자 인터페이스를 구성합니다.


다음 조치를 취했습니다.

  1. 각 버튼을 클릭합니다.
  2. liveData 또는 Flow 변수에 데이터를 삽입합니다.
  3. 변수 2번에서 watch를 실행하고 watcher가 되면 빈 액티비티를 엽니다.
  4. 다시 이전 활동으로 돌아가 변수 2번에 저장된 값을 확인합니다.

LiveData만으로 데이터 처리

ViewModel 구성

class LiveDataViewModel: ViewModel() {
    private val _someData = MutableLiveData<Int>()
    val someData: LiveData<Int> = _someData

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

테스트를 위한 활동 구성

class FirstActivity : AppCompatActivity() {
    private lateinit var binding: ActivityFirstBinding
    private val liveDataViewModel: LiveDataViewModel by viewModels()
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityFirstBinding.inflate(layoutInflater)
        setContentView(binding.root)

        lifecycleScope.launch {
            liveDataViewModel.someData.observe(this@FirstActivity) {
                Timber.d("data observed: $it")
                startActivity(Intent(this@FirstActivity, SecondActivity::class.java))
            }
        }

        binding.liveDataButton.setOnClickListener {
            liveDataViewModel.setData()
        }
    }

    override fun onResume() {
        super.onResume()
        Timber.d("liveDataViewModel Data: ${liveDataViewModel.someData.value}")
    }
}

검사 결과


직장에서 돌아왔다 기존 데이터가 보존된다는 것 당신은 확인할 수 있습니다

여기에 데이터를 남기지 않으려면 EventWrapper를 사용하십시오.


LiveData에 EventWrapper 적용

EventWrapper 클래스를 정의하여 화면으로 돌아올 때 다시한번 관찰불가

값을 초기화하는 방법 구글이 한때 사용한 방법이 있다.

여기서 언급하는 EventWrapper 클래스는 다음과 같이 정의할 수 있습니다.

/**
 * Used as a wrapper for data that is exposed via a LiveData that represents an event.
 */
open class Event<out T>(private val content: T) {

    var hasBeenHandled = false
        private set // Allow external read but not write

    /**
     * Returns the content and prevents its use again.
     */
    fun getContentIfNotHandled(): T? {
        return if (hasBeenHandled) {
            null
        } else {
            hasBeenHandled = true
            content
        }
    }

    /**
     * Returns the content, even if it's already been handled.
     */
    fun peekContent(): T = content
}

위의 EventWrapper를 사용하기 위한 ViewModel은 다음과 같이 정의됩니다.

class EventLiveDataViewModel: ViewModel() {
	// LiveData에 EventWrapper를 적용한다.
    private val _someData = MutableLiveData<Event<Int>>()
    val someData: LiveData<Event<Int>> = _someData

    fun setData() {
        if (_someData.value?.getContentIfNotHandled() == null) {
            _someData.value = Event(0)
        } else {
            _someData.value = Event(_someData.value?.getContentIfNotHandled() as Int + 1)
        }
    }
}

그리고 테스트할 활동은 다음과 같이 정의됩니다. (이제부터는 생략)

class FirstActivity : AppCompatActivity() {
    private lateinit var binding: ActivityFirstBinding
    private val liveDataViewModel: LiveDataViewModel by viewModels()
    private val eventLiveDataViewModel: EventLiveDataViewModel by viewModels()
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityFirstBinding.inflate(layoutInflater)
        setContentView(binding.root)

        lifecycleScope.launch {
            liveDataViewModel.someData.observe(this@FirstActivity) {
                Timber.d("data observed: $it")
                startActivity(Intent(this@FirstActivity, SecondActivity::class.java))
            }
        }

        lifecycleScope.launch{
            eventLiveDataViewModel.someData.observe(this@FirstActivity) {
                Timber.d("data observed: ${it.getContentIfNotHandled()}")
                startActivity(Intent(this@FirstActivity, SecondActivity::class.java))
            }
        }

        binding.liveDataButton.setOnClickListener {
            liveDataViewModel.setData()
        }

        binding.liveEventDataButton.setOnClickListener {
            eventLiveDataViewModel.setData()
        }
    }

    override fun onResume() {
        super.onResume()
        Timber.d("liveDataViewModel Data: ${liveDataViewModel.someData.value}")
        Timber.d("eventLiveDataViewModel Data: ${eventLiveDataViewModel.someData.value?.getContentIfNotHandled()}")
    }
}

그리고 eventLiveDataViewModel 버튼을 클릭하면 다음과 같은 로그가 출력됩니다.


화면이 처음 열리면 null을 반환합니다.

관찰 후 다른 활동으로 전환했다가 복귀한 후에도 값이 지속됩니다. 다시 제로입니다.

이것은 EventWrapper 때문입니다. 한 번 시청 null을 반환하기 때문입니다.

getContentIfNotHandled ~ 아니다 peekContent 이를 사용하여 값을 가져오면 일반 LiveData를 사용하는 것처럼 사용할 수 있습니다.


MVVM에서 viewModel 이벤트를 수신하는 방법 요약

  1. LiveData만 있는 이벤트 처리
  2. EventFlow를 LivieData에 래핑하여 처리

다음 포스트에서는 SingleLiveData와 SharedFlow에 대해 살펴보겠습니다.