Android DataBinding:再見Presenter,你好ViewModel!
最近一段時間MVP模式已經成為Android應用開發UI層架構設計的主流趨勢。類似TED MOSBY,nucleus和mortar之類的框架都引入了Presenters來幫助我們搭建簡潔的app架構。它們也(在不同的程度上)幫助我們處理Android平臺上臭名昭著的裝置旋轉和狀態持久化等問題。MVP模式也有助於隔離樣板程式碼,雖然這並不是MVP模式的設計初衷。
在Google I/O 2015上,伴隨著Android M預覽版釋出的Data Binding相容函式庫改變了這一切。
根據維基百科上關於MVP的詞條描述,Presenter作用如下:
Presenter作用於model和view,它從倉庫(Model)中獲取資料,並格式化後讓view進行顯示。
Data Binding框架將會接管Presenter的主要職責(作用於model和view上),Presenter的其他剩餘職責(從倉庫中獲取資料並進行格式化處理)則由ViewModel(一個增強版的Model)接管。ViewModel是一個獨立的Java類,它的唯一職責是表示一個View後面的資料。它可以合併來自多個數據源(Models)的資料,並將這些資料加工後用於展示。我之前寫過一篇關於ViewModel的短文,講述了它與Data Model或者Transport Model之間的區別。
我們今天要講述的架構是MVVM(Model-View-ViewModel),它最初是在2005年(不要嚇到哦)由微軟提出的一個被證明可用的概念。下面我將舉例說明從MVP到MVVM的改變,容我盜用下Hanne Dorfmann在他介紹TED MOSBY框架的文章中的插圖。
可以看到對view中資料的所有繫結和更新操作都是通過Data Binding框架實現的。通過ObservableField類,View在model發生變化時會作出反應,在XML檔案中對屬性的引用使得框架在使用者操作View時可以將變化推送給對應的ViewModel。我們也可以通過程式碼訂閱屬性的變化,這樣可以實現例如當CheckBox被點選後,TextView被禁用這樣的功能。像這樣使用標準Java類來表示View的視覺狀態的一個很大優勢是明顯的:你可以很容易對這種視覺行為進行單元
上面關於MVP的插圖中有一個名為Presenter.loadUsers()的方法,這是一個命令。在MVVM中這些方法定義在ViewModel中。從維基百科文章中可以看到:
view model是一個抽象的view,它對外暴露公有的屬性和命令。
因此這可能跟你以前熟悉的東西有些不同。在MVP模式中models很可能只是純粹用於儲存資料的“啞”類。對於把業務邏輯放到Models或者View Models中的行為不要感到害怕。這是面向物件程式設計的核心準則。回到Presenter.loadUsers()函式,現在它是一個放在ViewModel中的函式,它可能被View的後置程式碼(code-behind)呼叫,或者被位於View的XML檔案中的資料繫結命令呼叫。如果android-developer-preview問題跟蹤裡面這個issue描述的問題得到支援的話。如果我們沒能得到資料繫結到命令功能的支援,那就只能使用以前的android:onClick語法,或者手動在view中新增監聽器了。
程式碼後置(code-behind),微軟的一個概念,經常與早期的ASP.NET或者WinForms聯絡在一起。我想它也可以作為Android上的一個描述術語,View由兩個元素組成:View的佈局檔案(XML)和後置程式碼(Java),這通常是指Fragments,Activities或者繼承自View.java的其他類。
處理系統呼叫
View的後置程式碼還需要完成一系列用例-初始化系統,開啟對話方塊的函式,或者任何需要引用Android Context物件的呼叫。但不要把這樣的程式碼呼叫放到ViewModel中。如果ViewModel包含
- 1
- 1
這段程式碼,說明你用錯了,千萬不要這麼做,好奇害死貓。
我還沒有完全決定解決這個問題的最好辦法,不過這是因為有幾個好的選擇。一個方法是通過在ViewModel中持有View的一個引用來儲存Mosby中的presenter元素。這個方案不會降低可測試性。但跟在Mosby中持有一個單獨的Presenter類不同,我堅持認為將View作為介面的具體實現可以起到簡化程式碼的作用。另一個方法可能是使用Square的Otto之類的事件匯流排機制來初始化類似
- 1
- 1
的命令。這將會很好的分離view和viewmodel,不過這是一件好事嗎?
我們不需要框架了嗎?
那麼Data Binding框架已經接管了類似Mosby或者Mortar等框架的工作了嗎?只是一部分。我希望看到的是這些框架進化或者新增分支變成MVVM型別的框架,這樣我們在充分利用Data Binding的同時,可以最低限度依賴第三方框架,並保持框架的小而美。雖然Presenter的時代可能已經結束了,但這些框架在管理宣告週期和view(或者ViewModel)的狀態持久化方面還在發揮作用,這一點並沒有改變。(如果Google引入一個LifeCycleAffected介面讓Fragment, Activity 和 View進行實現,那將是多麼酷的一件事!這個介面由一個名為addOnPauseListener()和addOnResumeListener()的函式,在我們例子中如何使用這個介面將留給你來實現。)
更新:最近了解到AndroidViewModel框架,它實際上可能很適合MVVM和Android的Data Binding。不過我還沒有時間試用它。
總結
當我首次聽說Android M致力於改進SDK並重點關注開發者時,我真的很激動。當我聽說他們引入了Data Binding,我被震驚了。在其他平臺如WinForms, WPF, Silverlight 和 Windows Phone上面我已經用了好幾年Data Binding技術。我知道這可以幫助我們寫出簡潔的架構和更少的樣板程式碼。這個框架是站在開發者這邊的,而不是阻礙我們的,很久以前我就感受到這一點了。
但Data Binding不是銀彈,它也有缺點。在XML檔案中定義繫結本身就是一個問題。XML不會被編譯,它也不能進行單元測試。因此你將會經常在執行時才發現錯誤,而不是在編譯期間。忘記將屬性繫結到View了?很不幸。但工具可以發揮很大的幫助-這是為什麼我希望Google能夠儘量讓Android Studio最大程度支援Data Binding。XML繫結的語法和引用檢查,自動完成和導航支援。XML欄位的重新命名支援。從我測試Android Studio 1.3 beta來看,我至少可以肯定他們有在考慮這件事情。某些功能已經支援了,但還有很多沒有支援,不過1.3版本仍然處於beta階段,我們可以有更多的期待。
程式碼示例
接下來我將給出一個示例,演示從MVP架構遷移到MVVM架構的結果。在MVP版本工程中,我使用Mosby框架並使用Butterknife實現檢視注入。在MVVM例子中我使用Android M Data Binding並移除工程中對Mosby和Butterknife的依賴。結果是Presenter可以丟掉了,Fragment中程式碼減少了,不過ViewModel接管了很多程式碼。
在這個例子中我直接引用View來生成toast訊息。這也許不是我以後提倡的一種方法, 但理論上這麼做沒什麼問題。使用Robolectric和Mockito來對Fragment進行mock,這樣是可測試的,而且不會洩露記憶體,除非你錯誤的引用了ViewModels。
下面這個app只是起一個演示的作用,它具有一個簡單的登陸頁面,後臺會載入一些非同步資料,views之間會有一些依賴。
如果你希望在Android Studio中閱讀程式碼,可以到Github上分別檢出MVP和MVVM的標籤。
下面準備好接受程式碼轟炸吧��
MVP – VIEW – XML
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
MVP – VIEW – JAVA