一. 什么是 MVVM ?
MVVM 是前端一款數(shù)據(jù)驅(qū)動的架構(gòu)
- Model: 網(wǎng)絡(luò)接口數(shù)據(jù), 本地緩存數(shù)據(jù)等
- View: Activity, Fragment, DialogFragment.....
- ViewModel: 提供數(shù)據(jù)源, 處理業(yè)務(wù)邏輯
二. MVVM 與 MVP 的區(qū)別是什么?
MVVM 與 MVP 十分的相似, MVVM 是從 MVP 架構(gòu)演進(jìn)而來的
- MVP 通過 Presenter 做到了數(shù)據(jù)和視圖的解耦, 但是 Presenter 層的任務(wù)就變得十分之重, View 與 Presenter 交互要寫很多的接口, 接口粒度的控制也不容易把控
- ViewModel 是為了解決 MVP 中 Presenter 過重這個問題而生的, ViewModel 和 View 之間的通信并不需要通過接口來回進(jìn)行數(shù)據(jù)導(dǎo)入導(dǎo)出, 而是通過注冊觀察者, 當(dāng)數(shù)據(jù)變更時會自動的反應(yīng)到與之綁定的 View 上
可以看到 MVVM 解決了 MVP 中最影響開發(fā)效率的一個環(huán)節(jié), 它可以使用更加整潔的代碼, 讓開發(fā)變的更加的高效
三. 如何使用 MVVM
一) Model
// 全局的數(shù)據(jù)倉庫
interface DataSource : LocalDataSource, RemoteDataSource {
companion object {
val INSTANCE: DataSource = DataRepository()
}
}
// 唯一實現(xiàn)類
class DataRepository : DataSource
Model 層的設(shè)計非常簡單, 即一個全局的數(shù)據(jù)倉庫
二) View
1. Activity
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.main_activity)
if (savedInstanceState == null) {
supportFragmentManager.beginTransaction()
.replace(R.id.container, MainFragment.newInstance())
.commitNow()
}
}
}
Activity 為應(yīng)用殼, 布局也十分簡單, 直接填充了一個 Fragment
2. Fragment
class MainFragment : Fragment() {
companion object {
fun newInstance() = MainFragment()
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
// 這里 View 的創(chuàng)建交由 DataBinding 完成
val dataBinding: MainFragmentBinding = MainFragmentBinding.inflate(inflater, container, false)
// DataBinding 中持有 View 和 ViewModel 的引用, 他們直接的交互由 DataBinding 生成類 MainFragmentBinding 來控制
dataBinding.view = this
dataBinding.viewmodel = ViewModelProviders.of(this).get(MainViewModel::class.java)
return dataBinding.root
}
}
Fragment 中 View 的實例化非常的有趣, 它是通過 DataBinding 創(chuàng)建的, 并且通過 DataBinding 讓 View 層與 ViewModel 建立聯(lián)系, 這相當(dāng)于將 MVP 中 View 與 Presenter 的交互交由了系統(tǒng)控制
3. xml
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tool="http://schemas.android.com/tools">
<data>
<import type="android.view.View"/>
<variable
name="view"
type="com.sharry.demo.mvvm.ui.main.MainFragment"/>
<variable
name="viewmodel"
type="com.sharry.demo.mvvm.ui.main.MainViewModel"/>
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="@+id/btnCenter"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAllCaps="false"
android:text="@{viewmodel.messageText}"
android:onClick="@{() -> viewmodel.messageClicked()}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tool:text="I'm a fragment."
/>
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
xml 中指定了 View 與 ViewModel, 并且讓控件的屬性直接與 ViewModel 中的數(shù)據(jù)綁定, 當(dāng) ViewModel 中數(shù)據(jù)變更后便會直接反應(yīng)到控件上去
三) ViewModel
class MainViewModel : ViewModel() {
val messageText = ObservableField<String>()
private val dataSource = DataSource.INSTANCE
init {
messageText.set("I'm a fragment.")
}
fun messageClicked() = messageText.set("Text changed by DataBinding.")
}
ViewModel 中的代碼也非常的簡單, 直接構(gòu)建了一些需要用的 ObservableXXX 類型的數(shù)據(jù), 讓其變更時, DataBinding 會直接通知 View 更新
UML 圖
四. 總結(jié)
從 MVVM 的示例實現(xiàn)上可知, DataBinding 在 MVVM 的架構(gòu)中起到了不可或缺的作用, 它將原本 MVP 中 View 與 Presenter 的交互轉(zhuǎn)移給了 DataBinding, 讓系統(tǒng)幫我們?nèi)ゾS護(hù)復(fù)雜的交互流程
優(yōu)勢
- 開發(fā)者無需關(guān)心 ViewModel 與 View 的交互, 這一操作交由了 DataBinding 完成, 因此開發(fā)者可以更高效的進(jìn)行業(yè)務(wù)代碼的開發(fā)
- ViewModel 與 View 之間沒有無依賴, 因此 ViewModel 的可移植性要大大的強(qiáng)于 Presenter
劣勢
- 需要在 xml 中添加額外的代碼, 增加了額外的操作
- View 數(shù)據(jù)的注入操作隱藏在 DataBinding 的生成類中, 增加了排查問題的難度