---
# System prepended metadata

title: MVVM 架構
tags: [Android]

---

###### tags: `Android`
# MVVM 架構
在學習 MVVM 之前，可以先觀看 [Android 三種開發架構，MVC、MVP、MVVM怎麼選？](https://www.youtube.com/watch?v=nViX8m7gS7I) 來認識除了 MVVM 之外還有哪些架構，以及為什麼我們應該選擇使用 MVVM 架構。


Model：管理資料來源，如 Web API、本地資料庫
View：顯示 UI 和接收使用者動作
ViewModel：接收 View 的指令，並從 Model 取得 View 所需的資料

## 建立基本的 MVVM
建立 Model：==DataModel==
```kotlin=
class DataModel {

    interface onDataReadyCallback {
        fun onDataReady(data: String)
    }

    //模擬資料獲取時的延遲
    fun retrieveData(callback: onDataReadyCallback) {
        Handler().postDelayed({
            callback.onDataReady("New Data")
        }, 1000)
    }
}
```

建立 ViewModel：==MainViewModel==
```kotlin=
class MainViewModel {
    private val dataModel = DataModel()

    fun refresh() {
        dataModel.retrieveData(object : DataModel.onDataReadyCallback {
            override fun onDataReady(data: String) {
                //TODO("Use observable to update UI")
            }
        })
    }
}
```

建立 View：==MainActivity、activty_main==
```kotlin=
class MainActivity : AppCompatActivity() {

    private lateinit var viewModel: MainViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        viewModel = MainViewModel()

        btn_refresh.setOnClickListener {
            viewModel.refresh()
        }
    }
}
```

```xml=
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <Button
        android:id="@+id/btn_refresh"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Refresh"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintTop_toTopOf="parent"/>

    <ProgressBar
        android:id="@+id/progressBar"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>
```

這樣就完成基本的 MVVM 架構，剩下的就是 View 如何接收資料並更新 UI
而這個部分會使用 Data Binding 來完成 View 與 ViewModel 的溝通。

==為什麼要使用 Data Binding?==
MVVM 是使用觀察者的方式達成，以前有人會自己寫觀察者模式來處理
但有了 ObservableField 後就省事很多，而在 Android 發展出 Data Binding 後
也將原本的 ObservableField 納入 Data Binding 函式庫中。

## Jetpack Data Binding
Data Binding 可以看[此篇教學](https://hackmd.io/USJfeNs2R1qLGp6rkEJQiw)

## Jetpack ViewModel
完成 DataBinding 後，目前的架構還需要處理生命週期問題，如螢幕旋轉後資料遺失，而 Jetpack 的 ViewModel 能解決這問題，它有 lifecycle-aware 的特性，不會因 Activity 被重建而清除，在每次重建都能持有相同實體。

ViewModel 可以看[此篇教學](https://hackmd.io/ROW4JBjqRTOrwLDltAeZDQ)

## Jetpack LiveData
由於 Data Binding 沒有 Lifecycle-aware 的特性，因此 View 在背景時仍會觸發更新，而 Jetpack 的 LiveData 能解決這問題，它有 lifecycle-aware 的特性，能確保 Activity、Fragment 只在活耀的狀態才會收到資料的變化。

LiveData 可以看[此篇教學](https://hackmd.io/Peh0693lSQSjpvBzjaMcIg?view)



