公司的項目一直是以 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
兩者都是 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() 傳過去。
在 Android Studio 3.2 以前,databinding 須要添加一個 kapt 的 databinding-compiler 依賴,具體的版本號也須要對應,不然編譯後沒法生成 Binding 類,就會出現 cannot resolve symbol xxxxBinding 的錯誤。
在 Android Studio 3.2 以後正好相反,若是不刪除這個依賴就會報錯。
具體描述能夠看這個: stackoverflow.com/questions/5…
直接和數據打交道的 View 很容易轉換成使用 databinding 的代碼,但 RecyclerView 這種須要一個 adapter 的 View 就會面臨許多麻煩。github 上有些針對列表類 View 使用 databinding 的框架, 甚至能夠作到不用再寫 adapter,大多數場景下是能夠用的,惟一的缺點是 debug 艱難。
可是我沒選擇這種方案,當 RecyclerView 中存在複雜的交互邏輯時,使用 databinding 會產生特別多的狀態數據,反而讓代碼更加複雜了。不改變 adapter 的寫法,能夠用 LiveData 在 Activity 中把值傳到 adapter 中。
大多數 Data 都表示一個狀態,好比 Dialog 是否展現,TextView 的文本內容等。但有些特殊的 View 層變化只須要一個觸發,好比 Toast 和 SnackBar。
從原則上看,ViewModel 顯然不該該處理 Toast,但 Toast 這種很是輕量又能夠用 ApplicationContext 來實現的,稍微違背一下規則我我的也能接受……
固然,不能推薦你們也都這麼搞,google sample 的 architecture 項目中有一個SingleLiveEvent,能夠直接複製出來用。項目地址:github.com/googlesampl…
何時均可以用。雖然對於簡單的業務邏輯來講代碼量會增長,可是 MVVM 的思考方式會讓代碼更容易維護。過去代碼都寫在 Activity 中時,方法的拆解一般只考慮功能的相關程度和代碼行數,不會刻意區分 View 和邏輯。後期通過不一樣的人修改 bug,就會愈來愈混亂。
在團隊中推廣新的技術一貫都不容易,好在 MVVM 學習成本不高,帶來的好處也是顯而易見的。用一點時間重寫一遍項目中別人最不肯意改的頁面,而後分享一下代碼就行了。