# (Android) MVVM架構如何從viewModel發送event給view
> 方法1. 單純使用LiveData (不好)
> 問題1. View生命週期重置時,會收到之前的event
```kotlin=
class DemoViewModel {
val event = MutableLiveData<String>()
fun sendEvent(){
event.postValue("new event post")
}
}
class DemoFragment : Fragment() {
private val viewModel = DemoViewModel()
fun main(){
viewModel.event.observe(viewLifecycleOwner, Observer {
println("received event : $it")
})
}
}
```
---
> 方法2. 用變數判斷有無處理過(還是不好)
> 問題1. 忘記重置handled變數
> 問題2. View不應該處理邏輯
```kotlin=
class DemoViewModel {
var handled = false
val event = MutableLiveData<String>()
//...
}
class DemoFragment : Fragment() {
private val viewModel = DemoViewModel()
fun main(){
viewModel.event.observe(viewLifecycleOwner, Observer {
if(!viewModel.handled)
println("received event : $it")
viewModel.handled = true
})
}
}
```
---
> 方法3. 使用SingleLiveEvent(尚可)
> 使用Hack作法讓LiveData只發送一次更新
> Google已放棄此種做法
https://github.com/android/architecture-samples/blob/dev-todo-mvvm-live/todoapp/app/src/main/java/com/example/android/architecture/blueprints/todoapp/SingleLiveEvent.java
---
> 方法4. 封裝Event(Google推薦)
> 對Event取值時一併處理Handled狀態
```kotlin
/**
* 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
}
```
```kotlin=
class DemoViewModel {
val event = MutableLiveData<Event<String>>()
//...
}
class DemoFragment : Fragment() {
private val viewModel = DemoViewModel()
fun main(){
viewModel.event.observe(viewLifecycleOwner, Observer {
it.getContentIfNotHandled()?.let { data ->
println("received event : $data")
}
})
}
}
```
仔細看,View還是處理了邏輯
View在判斷getContentIfNotHandled()是否為空值,只是換了個寫法
正當我心想Google終究還是違反了原則
發現Google還搭配了一個EventObserver
```kotlin=
/**
* An [Observer] for [Event]s, simplifying the pattern of checking if the [Event]'s content has
* already been handled.
*
* [onEventUnhandledContent] is *only* called if the [Event]'s contents has not been handled.
*/
class EventObserver<T>(private val onEventUnhandledContent: (T) -> Unit) : Observer<Event<T>> {
override fun onChanged(event: Event<T>?) {
event?.getContentIfNotHandled()?.let { value ->
onEventUnhandledContent(value)
}
}
}
```
改用EventObserver去觀察Event的話,View的邏輯就被抽離了
最後View程式碼的如下
```kotlin=
class DemoFragment : Fragment() {
private val viewModel = DemoViewModel()
fun main(){
viewModel.event.observe(viewLifecycleOwner, EventObserver {
println("received event : $it")
})
}
}
```