[Android] 記一次 MVVM 實踐

背景:爲何選擇了MVVM

公司的項目一直是以 Activity 爲載體的 Android 式 MVC 架構,上手快,大多數頁面代碼也挺容易讀的。只是某複雜業務的 Activity 會有上千行的代碼,內部複雜的狀態判斷和異步邏輯特別多,並且原做者早已離職,每次提測都只能祈求這裏不出 bug。android

爲了重構這裏的代碼,引入 MVP 或 MVVM 是比較合適的方案。精簡原有的邏輯也能夠必定程度上增長代碼的可讀性,但仍是難以免單個 Activity 中存在大量代碼的問題。MVP 彷佛是 Android 開發中更加流行的方案(樣本數爲2,兩個同事都用過),但 MVP 的問題也是可預見的。剛好 Google 在 Jetpack 的 Architecture 中又宣傳了一波 MVVM,跟大佬們商量以後,我決定先改寫一個 Activity 試試。git

MVC/MVP/MVVM 的介紹就省略了,下面簡單說明一下用到的技術。github

知識準備:數據綁定方法

數據綁定到 View 是 MVVM 的核心,對於不一樣的應用場景須要使用不一樣的數據綁定手段:android-studio

  • databinding 是常被說起的數據綁定框架,應用於將數據直接綁定到 layout 文件,能夠有效的給 Activity 減負。
  • LiveData 是另外一種 Google 推薦的方案,當 LiveData 做爲數據源,既能夠結合 databinding 將數據變化更新到 layout 中,也能夠在 Activity 中設置 Observe 回調,實現更多 layout 中作不到的 View 效果(好比顯示一個 dialog)。

兩者都是 Google 出品,具體如何使用就不贅述了,後面會單獨把遇到的坑記錄下來。網絡

實踐過程簡述

先簡單說一下功能吧,改動以前的代碼就不貼了,畢竟是公司的項目。架構

核心流程大概是這樣的,還有一些暫停、模式切換和 postDelay 製造的延遲效果,整個流程中充滿了各類異步操做,每一個異步操做的回調中都須要判斷一連串的狀態,好比頁面是否在顯示中,音頻是否正在播放等。框架

爲了改寫成 MVVM 模式的代碼,首先要區分開哪些屬於 View 層,哪些屬於 ViewModel 層。只聲明主要流程的話,內容有如下幾點:異步

功能點 View 層 ViewModel 層
倒計時321 顯示倒計時的 Dialog 每秒更新剩餘時間(321)
播音頻 顯示播放進度條(%) 控制播放及回調處理
錄音 顯示錄音進度條(時長等於原音頻,並不是手動中止) 控制錄音及回調處理,錄音計時中止
提交數據 提交中顯示 loading 發起網絡請求提交數據
暫停 顯示暫停 Dialog,恢復其餘 View 中止當前操做,回到當前題目最初狀態

稍微分析一下,除了 Dialog 以外的 View 改動儘可能使用 databinding 直接放在 layout 中,Dialog 的顯示和隱藏都由一個 Boolean 類型的 LiveData 來控制,Activity 中基本只須要寫 ViewModel/databinding 的初始化代碼和 Dialog 相關代碼。post

ViewModel 中是所有的業務邏輯代碼,但不持有任何 View 相關的實例,全部的改變都做用於數據便可。另外,因爲 databinding 和 LiveData 都是對生命週期友好的框架,在 View 層銷燬後更新數據並不會致使崩潰,在充滿異步操做的邏輯中能夠節省不少個 if 判斷。學習

中途發現這個 RecyclerView 的交互太複雜了,databinding 中須要大量的 if 判斷,爲了增強代碼的可讀性,就把 adapter 相關的綁定採起 LiveData 實現了。最終 ViewModel 只有如下幾個可觀察數據:

簡單畫了一下重寫先後的主流程圖

修改前的

修改後的

提交後跳轉頁面的 Intent 須要傳遞多個數據,這裏用了一個 Bundle 類型的 LiveData,在 Activity 中直接使用 Intent.putExtras 把值放進去便可。同理,在 ViewModel 中初始化數據時也能夠把 getIntent().getExtras() 傳過去。

踩坑記錄

1. Kotlin 與 databinding 的兼容性問題以及不一樣 Android Studio 版本的區別。

在 Android Studio 3.2 以前,databinding 須要添加一個 kapt 的 databinding-compiler 依賴,具體的版本號也須要對應,不然編譯後沒法生成 Binding 類,就會出現 cannot resolve symbol xxxxBinding 的錯誤。

在 Android Studio 3.2 以後正好相反,若是不刪除這個依賴就會報錯。

具體描述能夠看這個: stackoverflow.com/questions/5…

2. databinding 和 recyclerview 怎麼合做。

直接和數據打交道的 View 很容易轉換成使用 databinding 的代碼,但 RecyclerView 這種須要一個 adapter 的 View 就會面臨許多麻煩。github 上有些針對列表類 View 使用 databinding 的框架, 甚至能夠作到不用再寫 adapter,大多數場景下是能夠用的,惟一的缺點是 debug 艱難。

可是我沒選擇這種方案,當 RecyclerView 中存在複雜的交互邏輯時,使用 databinding 會產生特別多的狀態數據,反而讓代碼更加複雜了。不改變 adapter 的寫法,能夠用 LiveData 在 Activity 中把值傳到 adapter 中。

3. 只進行一次觸發的 View 操做如何處理。

大多數 Data 都表示一個狀態,好比 Dialog 是否展現,TextView 的文本內容等。但有些特殊的 View 層變化只須要一個觸發,好比 Toast 和 SnackBar。

從原則上看,ViewModel 顯然不該該處理 Toast,但 Toast 這種很是輕量又能夠用 ApplicationContext 來實現的,稍微違背一下規則我我的也能接受……

固然,不能推薦你們也都這麼搞,google sample 的 architecture 項目中有一個SingleLiveEvent,能夠直接複製出來用。項目地址:github.com/googlesampl…

完成後的一些我的理解

何時須要 MVVM?

何時均可以用。雖然對於簡單的業務邏輯來講代碼量會增長,可是 MVVM 的思考方式會讓代碼更容易維護。過去代碼都寫在 Activity 中時,方法的拆解一般只考慮功能的相關程度和代碼行數,不會刻意區分 View 和邏輯。後期通過不一樣的人修改 bug,就會愈來愈混亂。

如何推廣 MVVM

在團隊中推廣新的技術一貫都不容易,好在 MVVM 學習成本不高,帶來的好處也是顯而易見的。用一點時間重寫一遍項目中別人最不肯意改的頁面,而後分享一下代碼就行了。

相關文章
相關標籤/搜索