Android 開發中的架構模式 -- MVC / MVP / MVVM

預備知識

瞭解 Android 基本開發
看完本文能夠達到什麼程度
瞭解如何分析一個架構模式
掌握 MVC,MVP,MVVM 架構定義和實現

更多面試內容,面試專題,flutter視頻 全套,音視頻從0到高手開發。
關注GitHub:https://github.com/xiangjiana/Android-MS
免費獲取面試PDF合集
免費提供簡歷修改建議,獲取大廠面試PDF和視頻教程android

文章概覽

1、什麼是架構

關於架構的定義,其實在不少書籍和文章中都是不一樣的,很難作一個統一。這裏列舉兩個定義: git

在維基百科裏是這樣定義的程序員

軟件架構是一個系統的草圖。軟件架構描述的對象是直接構成系統的抽象組件。各個組件之間的鏈接則明確和相對細緻地描述組件之間的通信。在實現階段,這些抽象組件被細化爲實際的組件,好比具體某個類或者對象。 github

在 IEEE 軟件工程標準詞彙中是這樣定義的: 面試

架構是以組件、組件之間的關係、組件與環境之間的關係爲內容的某一系統的基本組織結構,以及指導上述內容設計與演化的原理。設計模式

在看過茫茫多的架構定義之後,我理解的架構是這樣的:服務器

1.爲了解決特定的問題而提出
2.按照特定的原則將系統總體進行模塊/組件/角色的劃分
3.創建模塊/組件/角色間的溝通機制

具體解釋一下,首先是要有特定的問題,沒有問題空談架構,彷彿是空中樓閣,沒有實用價值,而對應到不一樣的問題,會有不一樣的解決方式。
其次是模塊的劃分要根據特定的原則,沒有原則隨意劃分,也就無從去評估一個架構的好壞。最後就是模塊間的通訊機制,讓系統成爲一個總體.網絡

最後,架構模式,其實更多的是一種思想,一種規則,每每一種架構模式可能會有不一樣的實現方式,而實現方式之間,只有合適與否,並無對錯之分。數據結構

2、如何分析一種架構模式

上面咱們介紹了架構的定義,根據這個定義,咱們在後面分析架構模式的時候,也會從這三方面進行。架構

2.1.架構解決了什麼問題

知道了架構模式要解決的問題,咱們才能針對性的去看,去想其解決方法是否得當,是否合適。

2.2.架構模式是如何劃分角色的

架構中最重要的就是角色 / 模塊的劃分,理解了架構模式中的角色劃分,才能更好的理解其結構。

2.3.角色間是如何通訊的

角色間的通訊也是重要的。相同的角色劃分,採用不一樣的通訊方式,每每就構成了不一樣的架構模式。

角色間通訊咱們能夠理解爲數據的流向。在 Android 開發中,通訊中的數據能夠理解爲兩種,一種是數據結構,也就是網絡請求,本地存儲等通訊使用的 JavaBean,另外一種是事件,也就是控件產生的動做,包括觸摸,點擊,滑動等等。咱們在通訊過程當中,也主要關注這兩種數據。

3、常見的架構模式有哪些

對於咱們 Android 開發者來講,常見的架構模式基本上就是 MVC,MVP,MVVM,這三種也是開發 GUI 應用程序常見的模式。

除此以外還有 分層模式,客戶端-服務器模式(CS模式),主從模式,管道過濾器模式,事件總線模式 等等。

這篇文章仍是具體分析 MVC,MVP,MVVM 這三種架構模式。

4、不使用架構以前 App 是怎麼開發的

咱們在瞭解架構的定義之後,可能會想,爲何要用這些架構模式呢?在咱們不瞭解這些模式以前,也是同樣的開發。相似設計模式,其實架構模式的目的不是爲了讓應用軟件開發出來,而是讓結構更清晰,分工更明確,擴展更方便等等。

咱們能夠看看,在不使用架構模式以前咱們是怎麼開發的。
舉個簡單的栗子,咱們界面上有 EditTextTextViewButton 三個控件,要實現的功能也比較簡單:

1. EditText 接受用戶輸入的內容
2.處理用戶輸入的數據
3.數據處理後輸出到 TextView
4.點擊 Button 清空用戶的輸入

界面以下:

咱們看看不使用架構模式是怎麼開發的,也就是咱們通常經常使用的開發方式:

4.1. 首先在 xml 設計界面

<?xml version="1.0" encoding="utf-8"?>
    <LinearLayout 
      xmlns:android="http://schemas.android.com/apk/res/android"
      xmlns:app="http://schemas.android.com/apk/res-auto"
      xmlns:tools="http://schemas.android.com/tools"
      android:layout_width="match_parent"
      android:layout_height="match_parent"
      android:orientation="vertical"
      android:padding="10dp"
      tools:context=".MainActivity">

      <TextView
          android:id="@+id/titleText"
          android:layout_width="match_parent"
          android:layout_height="wrap_content"
          android:text="Normal" />

      <EditText
          android:id="@+id/edit"
          android:layout_width="match_parent"
          android:layout_height="50dp"
          android:textColor="@android:color/darker_gray" />

      <TextView
          android:id="@+id/msgText"
          android:layout_width="wrap_content"
          android:layout_height="30dp"
          android:layout_marginTop="10dp"
          android:text="default msg"
          android:textColor="@android:color/darker_gray" />

      <TextView
          android:id="@+id/clearText"
          android:layout_width="match_parent"
          android:layout_height="30dp"
          android:layout_marginTop="10dp"
          android:background="@color/colorPrimary"
          android:gravity="center"
          android:text="clear"
          android:textColor="@android:color/white" />
  </LinearLayout>

1.在 Activity / Fragment 中獲取 View,進行事件監聽
2.經過 View 事件獲取數據後進行處理
3.設置處理後的數據給 View

代碼以下:

class NormalFragment : Fragment() {
      companion object {
          fun newInstance(): Fragment {
              return NormalFragment()
          }
      }
      private val handler: Handler = Handler()

      override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
          return inflater.inflate(R.layout.architecture, container, false)
      }

      override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
          super.onViewCreated(view, savedInstanceState)
          titleText.text = "NORMAL"
          edit.addTextChangedListener(object : TextWatcher {
              override fun afterTextChanged(s: Editable?) {
                  handleData(s.toString())
              }

              override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
              }

              override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
              }
          })
          clearText.setOnClickListener {
              edit.setText("")
          }
      }

      // 數據的處理,真實狀況下多是網絡請求,磁盤存取,大量計算邏輯等等
      private fun handleData(data: String) {
          if (TextUtils.isEmpty(data)) {
              msgText.text = "default msg"
              return
          }
          msgText.text = "handle data ..."
          handler.removeCallbacksAndMessages(null)
          // 延遲來模擬網絡或者磁盤操做
          handler.postDelayed({
              msgText.text = "handled data: $data"
          }, 3000)
      }
  }

默認開發方式的缺點:

咱們來分析一下上面的代碼,一個明顯的特色就是處理邏輯都集中在了 Activity / Fragment 中,無論是對 View 的操做,仍是對數據的處理。帶來的問題就是 Activity / Fragment 中邏輯臃腫,後續擴展牽一髮而動全身。並且職責劃分不清晰,給後續維護也帶來了困難。

既然如此,咱們看看使用架構模式改造後是什麼樣子的。

5、MVC 架構

5.1 模式介紹

其實關於 MVC 架構,在不一樣的框架裏,實現會有些差異,這也正說明了架構是一種思想。咱們這裏選用一種比較主流的實現。

5.1.1. 解決什麼問題

咱們能夠看到,上面不使用架構進行開發,帶來的問題是 Activity / Fragment 邏輯臃腫,不利於擴展。因此 MVC 就要解決的問題就是:控制邏輯,數據處理邏輯和界面交互耦合。

這裏先插一個題外話,其實咱們做爲程序員,寫代碼不只要實現需求,還要讓代碼易讀,易擴展。這一點,每每也能體現功力,並非說使用了各類奇技淫巧纔是大神。

不知道你們是否有接觸過 Java Swing 桌面應用開發,在 Java Swing 中,界面 / 控件的設置,也是用 Java 代碼來實現的,若是不採用架構,最後的結果就是控制邏輯,數據處理以及頁面展現的代碼都集中在一個類中,讀者朋友們能夠想象一下,這樣的代碼簡直是難以維護

5.1.2. 如何劃分角色

爲了解決上面的問題,MVC 架構裏,將邏輯,數據,界面的處理劃分爲三個部分,模型(Model)-視圖(View)-控制器(Controller)。各個部分的功能以下:

  • Model 模型,負責數據的加載和存儲。
  • View 視圖,負責界面的展現。
  • Controller 控制器,負責邏輯控制。
5.1.3. 如何通訊(數據的流向)

咱們再看看三者之間是怎麼通訊的。

在介紹通訊以前,咱們先解釋一下通訊中的數據是什麼。其實在 Android 開發中,通訊數據能夠理解爲兩種,一種是數據結構,也就是網絡請求,本地存儲等通訊使用的 JavaBean,另外一種是事件,也就是控件產生的動做,包括觸摸,點擊,滑動等等。咱們在通訊過程當中,也主要關注這兩種數據。

MVC 架構中,View 產生事件,通知到 ControllerController 中進行一系列邏輯處理,以後通知給 Model 去更新數據,Model 更新數據後,再將數據結構通知給 View 去更新界面。

這就是一個完整 MVC 的數據流向

5.2 在 Android 中的具體實現

理解了 MVC 模式,咱們看看其具體實現。

其實在 Android 開發中,其自己默承認以理解爲 MVC 結構,把 View 放在 xml 中與 Java 代碼解耦,而後 Activity / Fragment 充當 Controller 進行邏輯控制,可是 Android 自己並無對 Model 進行劃分,因此每每咱們會讓 Activity / Fragment 充當 ModelController 兩個角色。並且每每 xml 中的 View 操做也是在 Activity / Fragment 中,致使有時候 Activity / Fragment 也會充當一些 View 的角色。

因此咱們在具體實現過程當中,要把職責劃分清楚,這裏咱們讓 Fragment 充當 View 的角色,把 ModelController 的邏輯劃分清楚。

咱們先定義三個接口以下:

// 數據模型接口,定義了數據模型的操做
  interface IModel {
      fun setView(view: IView)
      // 數據模型處理輸入的數據
      fun handleData(data: String)
      // 清空數據
      fun clearData()
  }

  // 視圖接口,定義視圖的操做
  interface IView {
      fun setController(controller: IController)
      // 數據處理中狀態
      fun dataHanding()
      // 數據處理完成,更新界面
      fun onDataHandled(data: String)
  }

  // 控制器接口,定義控制器的邏輯
  interface IController {
      fun setModel(model: IModel)
      // EditText 數據變化,通知控制器
      fun onDataChanged(data: String)
      // 清空按鈕點擊事件
      fun clearData()
  }

上面三個接口分別定義了 Model,View,Controller 的操做。有一點注意的是,根據 MVC 的通訊流程,View 須要持有 ControllerController 須要持有 ModelModel 須要持有 View,因此須要暴露相應的接口。

下面咱們看看具體的實現:

  • Model 的實現

Model 中對數據的處理是添加了 "handled data: " 前綴,並增長了 3 秒的延遲

class HandleModel : IModel {
      private var view: IView? = null
      private val handler: Handler = Handler(Looper.getMainLooper())

      override fun setView(view: IView) {
          this.view = view
      }

      // 接受到數據後,進行處理,這裏設置了 3 秒的延遲,模擬網絡請求處理數據的操做
      override fun handleData(data: String) {
          if (TextUtils.isEmpty(data)) {
              return
          }
          view?.dataHanding()
          handler.removeCallbacksAndMessages(null)
          // 延遲來模擬網絡或者磁盤操做
          handler.postDelayed({
              // 數據處理完成,通知 View 更新界面
              view?.onDataHandled("handled data: $data")
          }, 3000)
      }

      // 接收到清空數據的事件,直接清空數據
      override fun clearData() {
          handler.removeCallbacksAndMessages(null)
          // 數據清空後,通知 View 更新界面
          view?.onDataHandled("")
      }
  }
  • Controller 的實現

Controller 的實現比較簡單,將操做直接轉發給 Model,實際上,對於複雜的業務場景,這裏要處理不少業務邏輯。

class HandleController : IController {
      private var model: IModel? = null

      override fun onDataChanged(data: String) {
          model?.handleData(data)
      }

      override fun clearData() {
          model?.clearData()
      }

      override fun setModel(model: IModel) {
      }
  }
  • View 的實現

這裏 Fragment 充當了 View 的角色,主要負責將 View 的事件傳遞給 Controller,以及接受到 Model 的數據進行界面更新。

class MVCFragment : Fragment(), IView {

      companion object {
          fun newInstance(): Fragment {
              return MVCFragment()
          }
      }

      private val model: IModel = HandleModel()
      private var controller: IController = HandleController()

      override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
            return inflater.inflate(R.layout.architecture, container, false)
      }

      override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
          super.onViewCreated(view, savedInstanceState)
          setController(controller)
          model.setView(this)

          titleText.text = "MVC"
          edit.addTextChangedListener(object : TextWatcher {
              override fun afterTextChanged(s: Editable?) {
                  // 通知 Controller 輸入的數據產生變化
                  controller?.onDataChanged(s.toString())
              }

              override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
              }

              override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
              }
          })
          clearText.setOnClickListener {
              // 通知 Controller 清空數據事件
              controller?.clearData()
          }
      }

      // Model 數據變化,進行界面更新
      override fun onDataHandled(data: String) {
          if (TextUtils.isEmpty(data)) {
              edit.setText("")
              msgText.text = "default msg"
          } else {
              msgText.text = data
          }
      }

      // Model 數據變化,進行界面更新
      override fun dataHanding() {
          msgText.text = "handle data ..."
      }

      override fun setController(controller: IController) {
          this.controller = controller
      }
  }

這樣咱們就實現了一個簡單的 MVC 結構。

5.3 MVC 架構模式的優缺點
優勢:
  • 結構清晰,職責劃分清晰
  • 下降耦合
  • 有利於組件重用
缺點:
  • 其實咱們上述的示例,已是通過優化的 MVC 結構了,通常來講,Activity / Fragment 會承擔 ViewController 兩個角色,就會致使 Activity / Fragment 中代碼較多
  • Model 直接操做 ViewView 的修改會致使 ControllerModel 都進行改動
  • 增長了代碼結構的複雜性

6、MVP 架構

6.1 模式介紹

6.1.1. 解決什麼問題

MVP 要解決的問題和 MVC 大同小異:控制邏輯,數據處理邏輯和界面交互耦合,同時能將 MVC 中的 ViewModel 解耦。

6.1.2. 如何劃分角色

MVP 架構裏,將邏輯,數據,界面的處理劃分爲三個部分,模型(Model)-視圖(View)-控制器(Presenter)。各個部分的功能以下:

  • Model 模型,負責數據的加載和存儲。
  • View 視圖,負責界面的展現。
  • Presenter 控制器,負責邏輯控制
6.1.3. 如何通訊(數據的流向)

咱們能夠看到,MVP 中的各個角色劃分,和 MVC 基本上類似,那麼區別在哪裏呢?區別就在角色的通訊上。

MVPMVC 最大的不一樣,就是 ViewModel 不相互持有,都經過 Presenter 作中轉。View 產生事件,通知給 PresenterPresenter 中進行邏輯處理後,通知 Model 更新數據,Model 更新數據後,通知數據結構給 PresenterPresenter 再通知 View 更新界面。

這就是一個完整 MVP 的數據流向。

6.2 在 Android 中的實現

理解了 MVP 以後,咱們看一下其具體實現。

首先咱們定義三個接口:

// 模型接口,定義了數據模型的操做
  interface IModel {
      fun setPresenter(presenter: IPresenter)
      // 梳理數據
      fun handleData(data: String)
      // 清除數據
      fun clearData()
  }

   // 視圖接口,定義了視圖的操做
  interface IView {
      fun setPresenter(presenter: IPresenter)
      // 數據處理中視圖
      fun loading()
      // 數據展現
      fun showData(data: String)
  }

  // 控制器,定義了邏輯操做
  interface IPresenter {
      fun setView(view: IView)
      fun setModel(model: IModel)
      // Model 處理完成數據通知 Presenter
      fun dataHandled(data: String)
      // Model 清除數據後通知 Presenter
      fun dataCleared()
      // View 中 EditText 文字變化後通知 Presenter
      fun onTextChanged(text: String)
      // View 中 Button 點擊事件通知 Presenter
      fun onClearBtnClicked()
  }

上面定義了 View,Model,Presenter 三個接口,其中 ViewModel 會持有 PresenterPresenter 持有 View Model

接着看下接口的實現:

  • Model 的實現
class HandleModel : IModel {
    private var presenter: IPresenter? = null
    private var handler = Handler(Looper.getMainLooper())

    override fun handleData(data: String) {
        if (TextUtils.isEmpty(data)) {
            return
        }
        handler.removeCallbacksAndMessages(null)
        // 延遲來模擬網絡或者磁盤操做
        handler.postDelayed({
            // 數據處理完成,通知 Presenter
            presenter?.dataHandled("handled data: $data")
        }, 3000)
    }

    override fun clearData() {
        handler.removeCallbacksAndMessages(null)
        // 數據清理完成,通知 Presenter
        presenter?.dataCleared()
    }

    override fun setPresenter(presenter: IPresenter) {
        this.presenter = presenter
    }

 }

Model 的實現和前面 MVC 中的實現基本一致,不過在 MVCModel 直接操做 View 進行視圖展現,而在 MVP 裏,要通知 Presenter 去中轉。

  • View 的實現

這裏依舊是 Fragment 充當了 View 的角色,主要負責將 View 的事件傳遞給 Presenter,以及接受到 Presenter 的數據進行界面更新。

class MVPFragment : Fragment(), IView {

    companion object {
        fun newInstance(): Fragment {
            val presenter = Presenter()
            val fragment = MVPFragment()
            val model = HandleModel()
            fragment.setPresenter(presenter)
            model.setPresenter(presenter)
            presenter.setModel(model)
            presenter.setView(fragment)
            return fragment
        }
    }

    var mpresenter: IPresenter? = null

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        return inflater.inflate(R.layout.architecture, container, false)
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        titleText.text = "MVP"

        edit.addTextChangedListener(object : TextWatcher {
            override fun afterTextChanged(s: Editable?) {
                // 傳遞 文字修改 事件給 Presenter
                mpresenter?.onTextChanged(s.toString())
            }

            override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
            }

            override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
            }
        })
        clearText.setOnClickListener {
            // 傳遞按鈕點擊事件給 Presenter
            mpresenter?.onClearBtnClicked()
        }
    }

    override fun setPresenter(presenter: IPresenter) {
        this.mpresenter = presenter
    }

    // 展現數據處理中的視圖
    override fun loading() {
        msgText.text = "handling data ..."
    }

    // 展現處理後的數據
    override fun showData(data: String) {
        msgText.text = data
    }
 }
  • Presenter 的實現

這裏 Presenter 的實現比較簡單,沒有太多的業務邏輯,實際應用中,這裏會進行業務邏輯的處理。

class Presenter : IPresenter {
      private var model: IModel? = null
      private var view: IView? = null

      override fun setModel(model: IModel) {
          this.model = model
      }

      override fun setView(view: IView) {
          this.view = view
      }

      override fun dataHandled(data: String) {
          view?.showData(data)
      }

      override fun dataCleared() {
          view?.showData("")
      }

      override fun onTextChanged(text: String) {
          view?.loading()
          model?.handleData(text)
      }

      override fun onClearBtnClicked() {
          model?.clearData()
      }
   }

6.3 MVP 架構模式的優缺點

優勢:
  • 結構清晰,職責劃分清晰
  • 模塊間充分解耦
  • 有利於組件的重用
缺點:
  • 會引入大量的接口,致使項目文件數量激增
  • 增大代碼結構複雜性

7、MVVM 架構

7.1 模式介紹

7.1.1. 解決什麼問題

MVVM 要解決的問題和 MVCMVP 大同小異:控制邏輯,數據處理邏輯和界面交互耦合,而且同時能將 MVC 中的 ViewModel 解耦,還能夠把 MVPPresenterView 也解耦。

7.1.2. 如何劃分角色

MVVM 架構裏,將邏輯,數據,界面的處理劃分爲三個部分,模型(Model)-視圖(View)-邏輯(ViewModel)。各個部分的功能以下:

  • Model 模型,負責數據的加載和存儲。
  • View 視圖,負責界面的展現。
  • ViewModel 控制器,負責邏輯控制。
7.1.3. 如何通訊(數據的流向)

咱們能夠看到,MVP 中的各個角色劃分,和 MVCMVP 基本上類似,區別也是在於角色的通訊上。

咱們上面說到,在 MVP 中,就是 ViewModel 不相互持有,都經過 Presenter 作中轉。這樣可使 ViewModel 解耦。

而在 MVVM 中,解耦作的更完全,ViewModel 也不會持有 View。其中 ViewModel 中的改動,會自動反饋給 View 進行界面更新,而 View 中的事件,也會自動反饋給 ViewModel

要達到這個效果,固然要使用一些工具輔助,比較經常使用的就是 databinding

在 MVVM 中,數據的流向是這樣的

View 產生事件,自動通知給 ViewModeViewModel 中進行邏輯處理後,通知 Model 更新數據,Model 更新數據後,通知數據結構給 ViewModelViewModel 自動通知 View 更新界面。

這就是一個完整 MVVM 的數據流向。

7.2 在 Android 中的實現

MVVM 的實現會複雜一點,咱們先看下接口的定義:

// ViewModel 接口,定義了邏輯操做
  interface IViewModel {
      fun setModel(model: IModel)
      fun handleText(text: String?)
      fun clearData()
      fun dataHandled(data: String?)
      fun dataCleared()
  }

  // 模型接口,定義了數據操做
  interface IModel {
      fun setViewModel(viewModel: IViewModel)
      fun handleData(data: String?)
      fun clearData()
  }

MVVM 中的接口只定義了 ViewModelModel,沒有 View 接口,是由於 View 是經過 databindViewModel 的。

咱們再看看具體實現:

  • Model 實現

Model 的實現和上面基本一致,就是對數據的處理,處理完成後通知 ViewModel

class HandleModel : IModel {
      private var viewModel: IViewModel? = null
      private var handler = Handler(Looper.getMainLooper())

      override fun handleData(data: String?) {
          if (TextUtils.isEmpty(data)) {
              return
          }
          handler.removeCallbacksAndMessages(null)
          // 延遲來模擬網絡或者磁盤操做
          handler.postDelayed({
              // 數據處理完成通知 ViewModel
              viewModel?.dataHandled("handled data: $data")
          }, 3000)
      }

      override fun clearData() {
          handler.removeCallbacksAndMessages(null)
          // 數據清理完成通知 ViewModel
          viewModel?.dataCleared()
      }

      override fun setViewModel(viewModel: IViewModel) {
          this.viewModel = viewModel
      }
  }
  • ViewModel 實現

ViewModel 的實現要有些不一樣,咱們採用 databind 進行 ViewModelView 的綁定。

其中會定義兩個變量,inputText 是和 EditText 雙向綁定的數據,handledText 是和 TextView 雙向綁定的數據。

EditText 中輸入的數據有變化,會通知到 inputText 註冊的監聽器中,而 handledText 值的改變,會自動顯示到界面上。

class ViewModel : IViewModel {
      private var model: IModel? = null
      // View 綁定的數據,inputText 和 handledText 更新後會自動通知 View 更新界面
      var inputText: MutableLiveData<String> = MutableLiveData()
      var handledText: MutableLiveData<String> = MutableLiveData()

      init {
          // 註冊數據監聽,數據改變後通知 Model 去處理數據
          inputText.observeForever {
              handleText(it)
          }
          handledText.value = "default msg"
      }

      override fun handleText(text: String?) {
          if (TextUtils.isEmpty(text)) {
              handledText.value = "default msg"
              return
          }
          handledText.value = "handle data ..."
          model?.handleData(text)
      }
  
      // 清空按鈕的點擊事件綁定
      override fun clearData() {
          model?.clearData()
      }

      override fun setModel(model: IModel) {
          this.model = model
          model.setViewModel(this)
      }

      // Model 數據處理完成,設置 handledText 的值,自動更新到界面
      override fun dataHandled(data: String?) {
          handledText.value = data
      }

      // Model 數據處理完成,設置 inputText 的值,自動更新到界面
      override fun dataCleared() {
          inputText.value = ""
      }
  }
  • View 實現

看一下 View 中的數據綁定。

class MVVMFragment : Fragment() {
      companion object {
          fun newInstance(): Fragment {
              return MVVMFragment()
          }
      }
 
      override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
          // 使用 databind 進行數據綁定
          var binding: ArchitectureBindingBinding = DataBindingUtil.inflate(inflater, R.layout.architecture_binding, container, false)
          binding.lifecycleOwner = this
          val viewModel = ViewModel()
          viewModel.setModel(HandleModel())
          binding.viewmodel = viewModel
          return binding.root
      }
  }
<?xml version="1.0" encoding="utf-8"?>
  <layout 
  xmlns:android="http://schemas.android.com/apk/res/android"
      xmlns:app="http://schemas.android.com/apk/res-auto"
      xmlns:tools="http://schemas.android.com/tools">

      <!--定義 View 中綁定的數據-->
      <data>
          <variable
              name="viewmodel"
              type="com.zy.architecture.mvvm.ViewModel" />
      </data>

      <LinearLayout
          android:layout_width="match_parent"
          android:layout_height="match_parent"
          android:orientation="vertical"
          android:padding="10dp"
          tools:context=".MainActivity">

          <TextView
              android:id="@+id/titleText"
              android:layout_width="match_parent"
              android:layout_height="wrap_content"
              android:text="MVVM" />

          <!--雙向綁定 inputText 到 EditText-->
          <EditText
              android:id="@+id/edit"
              android:layout_width="match_parent"
              android:layout_height="50dp"
              android:text="@={viewmodel.inputText}" 
              android:textColor="@android:color/darker_gray" />

          <!--綁定 handledText 到 TextView-->
          <TextView
              android:id="@+id/msgText"
              android:layout_width="wrap_content"
              android:layout_height="30dp"
              android:layout_marginTop="10dp"
              android:text="@{viewmodel.handledText}"
              android:textColor="@android:color/darker_gray" />

          <!--綁定清空數據的點擊事件 到 TextView-->
          <TextView
              android:id="@+id/clearText"
              android:layout_width="match_parent"
              android:layout_height="30dp"
              android:layout_marginTop="10dp"
              android:background="@color/colorPrimary"
              android:gravity="center"
              android:onClick="@{() -> viewmodel.clearData()}"
              android:text="clear"
              android:textColor="@android:color/white" />
      </LinearLayout>
  </layout>

經過上面的實現,當 EditText 中文字變化後,會自動修改 inputText 的值,觸發 inputText 監聽器,此時 ViewModel 將消息傳遞給 Model 進行處理,Model 數據處理完成後,通知 ViewModel 更新 handledText 的值,自動更新到界面上。

點擊清空按鈕時,自動調用綁定的點擊函數,通知 ViewModel 清空事件,ViewModel 將消息傳遞給 Model 進行數據清空,Model 數據處理完成後,通知 ViewModel 進行界面更新。

7.3 MVVM 架構模式的優缺點

優勢:

  • 結構清晰,職責劃分清晰
  • 模塊間充分解耦
  • MVP 的基礎上,MVVMViewViewModel 也進行了解耦

缺點:

  • Debug 困難,因爲 ViewViewModel 解耦,致使 Debug 時難以一眼看出 View 的事件傳遞
  • 代碼複雜性增大

8、架構模式的運用

上面的文章中,咱們介紹了 MVC,MVP,MVVM 三種架構模式,以及其簡單的實現。這裏咱們再回過頭思考一下,何時該使用架構模式呢?

架構模式可使代碼模塊清晰,職責分工明確,容易擴展,帶來的反作用就是會引入大量的接口,致使代碼文件數量激增。

咱們在最開始說過,架構模式是用來解決特定的問題的,若是特定的問題在目前階段不是問題,或者不是主要問題,那麼咱們能夠先不考慮使用架構模式。好比一個功能很是簡單,代碼量少,然後續又沒有擴展的需求,那咱們直接使用傳統方式進行開發,快速且清晰,徹底沒有必要爲了架構而架構。

對於在開始沒有考慮架構模式的代碼,後續慢慢去重構,也是一個好的選擇。

總結來講就是:架構雖好,可不要貪杯哦~

總結

關於我:

更多面試內容,面試專題,flutter視頻 全套,音視頻從0到高手開發。
關注 GitHub: https://github.com/xiangjiana/Android-MS
免費獲取面試PDF合集
免費提供簡歷修改建議,獲取大廠面試PDF

相關文章
相關標籤/搜索