android UI設計MVVM設計模式討論?

因為google 剛出了一套databinding 框架來實現VM, 一些業務邏輯操作完全可以放入VM來做,還有一些邏輯操作與當前Context有關,可以通過介面實現讓activity 或者fragment來做,如果一個VM來做所有的操作,是不是代碼太多,還是按邏輯拆分幾個不同VM來做?


很不錯的問題。

今天早上跟群里的朋友們討論了一下MVVM,其中@肥肥魚慎重的表示沒有百分百把握不敢教壞小朋友。這幫人都這麼謙虛,那隻能我拋磚引玉了。

在傳統的框架中,提的最多的是MVC和MVP。其中MVC出現與上世紀70年代,在三十多年的工程實踐中,MVC充分證明了它的成功,同時在漫長的時間中演變出了許多變種,其中也包括MVP.

MVC和MVP最大的差別在與控制層對於整個框架的控制力上。Android中經常會出現數千行的Activity代碼,究其原因,我認為是Android中純粹作為View的各個XML視圖功能太弱,Activity基本上都是View和Controller的合體,既要負責視圖的顯示又要加入控制邏輯,承擔的功能過多,代碼量大也就不足為奇。所以我認為在Android上,MVP優於MVC,是因為我們需要更強力的控制層最大程度上分擔Activity中邏輯的部分,具體的思想可以參考我的博客:

Android App整體架構設計的思考(一)

MVVM可以算是MVP的升級版,其中的VM是ViewModel的縮寫,ViewModel可以理解成是View的數據模型和Presenter的合體,ViewModel和View之間的交互通過Data Binding完成,而Data Binding可以實現雙向的交互,這就使得視圖和控制層之間的耦合程度進一步降低,關注點分離更為徹底,同時減輕了Activity的壓力,三者之間的差別如下如所示:

回到題主的問題,VM中View的數據模型每個頁面當然只有一套,但是Presenter,我建議根據邏輯拆開。(剩下的等我周末體驗一下再更新)相關參考:

Data Binding Guide

對MVC、MVP、MVVM的理解

Data Binding 用戶指南(Android)


首先對 Google 在 Android 中引入了 Data Binding 的舉措表示點贊,在 Support 包中也出現了使用 MVP 的例子,請翻看 NavigationView 的源碼。

MVC -&> MVP -&> MVVM 這幾個軟體設計模式是一步步演化發展的,MVVM 是從 MVP 的進一步發展與規範,MVP 隔離了 M 與 V 的直接聯繫後,靠 Presenter 來中轉,所以使用 MVP 時 P 是直接調用 View 的介面來實現對視圖的操作的,這個 View 介面的東西一般來說是 showData、showLoading...M 與 V是隔離了,方便測試了,但代碼還不夠優雅簡潔啊,所以 MVVM 就彌補了這些缺陷。在 MVVM 中就出現的 Data Binding 這個概念,意思就是 View 介面的 showData 這些實現方法可以不寫了,通過 Binding 來實現。。

在 Android 中,我個人也是把 Activity、Fragment 當做 View 層的東西的,題主所說 」一些邏輯操作與當前Context有關「,除了彈 Dialog(而且這也是 View),其它情況基本可以自己重寫 Application 來拿 ApplicationContext,那麼題主所說的邏輯操作可以不在 Activity、Fragment 出現吧。VM 也是 M 與 V 的橋樑啊,怎麼會有很多操作呢~一般場景下一個 View 就會有一個相應的 VM。

最後分享一個網站,https://speakerdeck.com (Speakerdeck of GitHub Inc - 在線簡報、PDF分享展示平台!)以及下面的兩個與題目相關的PPT。

Clean Android Architecture:https://speakerdeck.com/richk/clean-android-architecture(這裡有 Coursera App Architecture 的設計分析哦,很贊,翻找 Video 中... ,Demo App 的 Github 傳送門:richk/CourseraDemoApp · GitHub)

How to use MVVM pattern in Android (Droidcon Krakow 2014):https://speakerdeck.com/radzio/how-to-use-mvvm-pattern-in-android-droidcon-krakow-2014

Last:Architecting Android…The clean way?(自行搜索譯文)

=================== Update ====================

寫了篇相關博客:MVVM_Android-CleanArchitecture 代碼:https://github.com/zhengxiaopeng/MVVM_Android-CleanArchitecture


關於Android MVVM 最近項目開發中一直在用,從DataBinding發布之後一直在關注,如今DataBinding 正式版終於發布了(相信可能又會掀起Android MVVM的一場熱潮)鑒於之前的一段MVVM的實踐開發經驗,這邊來回答 題主可能t的2個問題:

1、一些業務邏輯操作完全可以放入VM來做,還有一些邏輯操作與當前Context有關,可以通過介面實現讓activity 或者fragment來做?

是否讓一些邏輯操作通過介面讓activity 或者fragment來做這個問題,主要是看你所謂的邏輯操作是什麼,在MVVM 設計框架的時候,我個人是傾向把各層職責分得比較開,每一層只做自己的事情。下面看下我個人理解的每一層的職責和分工。

  • View

    View層做的就是和UI相關的工作,我們只在XML和Activity或Fragment寫View層的代碼,View層不做和業務相關的事,也就是我們的Activity 不寫和業務邏輯相關代碼,也不寫需要根據業務邏輯來更新UI的代碼,因為更新UI通過Binding實現,更新UI在ViewModel裡面做(更新綁定的數據源即可),Activity 要做的事就是初始化一些控制項(如控制項的顏色,添加 RecyclerView 的分割線),Activity可以更新UI,但是更新的UI必須和業務邏輯和數據是沒有關係的,只是單純的根據點擊或者滑動等事件更新UI(如 根據滑動顏色漸變、根據點擊隱藏等單純UI邏輯),Activity(View層)是可以處理UI事件,但是處理的只是處理UI自己的事情,View層只處理View層的事。簡單的說:View層不做任何業務邏輯、不涉及操作數據、不處理數據、UI和數據嚴格的分開。
  • ViewModel

    ViewModel層做的事情剛好和View層相反,ViewModel 只做和業務邏輯和業務數據相關的事,不做任何和UI、控制項相關的事,ViewModel 層不會持有任何控制項的引用,更不會在ViewModel中通過UI控制項的引用去做更新UI的事情。ViewModel就是專註於業務的邏輯處理,操作的也都是對數據進行操作,這些個數據源綁定在相應的控制項上會自動去更改UI,開發者不需要關心更新UI的事情。關於對UI控制項事件的處理,我們也希望能把這些事件處理綁定到控制項上,並把這些事件統一化,方便ViewModel對事件的處理和代碼的美觀。為此我們通過BindingAdapter 對一些常用的事件做了封裝,把一個個事件封裝成一個個Command,對於每個事件我們用一個ReplyCommand&去處理就行了,ReplyCommand&會把可能你需要的數據帶給你,這使得我們處理事件的時候也只關心處理數據就行了,再強調一遍ViewModel 不做和UI相關的事。
  • Model

    Model 的職責很簡單,基本就是實體模型(Bean)同時包括Retrofit 的Service ,ViewModel 可以根據Model 獲取一個Bean的Observable&( RxJava ),然後做一些數據轉換操作和映射到ViewModel 中的一些欄位,最後把這些欄位綁定到View層上。

上面三部分的分工很明確,要不要把一部分邏輯放到activity 或者fragment來做是取決於你的提到的操作邏輯是什麼,如果這些邏輯操作是可以通過修改數據(這些數據綁定到UI)來更改UI或者你的操作邏輯是業務邏輯或者修改數據,那麼這塊邏輯你完全可以在ViewModel 裡面做。如果這些邏輯操作只是和UI有關,而且不能通過Binding的方式更改數據源去反饋到UI(比如說 簡單的對話框、PopupWindow等)是可以考慮放到View層去做,但是如果這部分操作邏輯涉及到業務和數據相關的,那麼建議不用放到View層做,View層主要職責是和UI相關的、沒有數據,沒有業務。

2、如果一個VM來做所有的操作,是不是代碼太多,還是按邏輯拆分幾個不同VM來做?

這個問題先看下下面的這張圖:

上圖反應了MVVM框架中各個模塊的聯繫和數據流的走向,我們別的模塊先不管,我們直接來看我們的ViewModel模塊,在ViewModel類我傾向於只包含下面5中類型的數據和欄位:

1、Context (上下文)
2、Model (數據模型Bean)
3、Data Field (數據綁定)
4、Command (命令綁定)
5、Child ViewModel (子ViewModel)

至於為什麼推薦只包含這5種類型的數據欄位和這些數據欄位分別是做什麼用的,請查看我最新的一盤關於博客:如何構建Android MVVM應用程序,接下來說重點:看到第5條沒有(子ViewModel)

  • Child ViewModel (子ViewModel)

    子ViewModel 的概念就是在ViewModel 裡面嵌套其他的ViewModel,這種場景還是很常見的。比如說你一個Activity裡面有兩個Fragment,ViewModel 是以業務劃分的,兩個Fragment做的業務不一樣,自然是由兩個ViewModel來處理,Activity 本身可能就有個ViewModel 來做它自己的業務,這時候Activity的這個ViewModel裡面可能包含了兩個Fragment分別的ViewModel。這就是嵌套的子ViewModel。還有另外一種就是對於AdapterView 如ListView RecyclerView,ViewPager等。

    //Child ViewModel
    public final ObservableList& itemViewModel = new ObservableArrayList&<&>();

    它們的每個Item 其實就對應於一個ViewModel,然後在當前的ViewModel 通過ObservableList&持有引用(如上述代碼),這也是很常見的嵌套的子ViewModel。我們其實還建議,如果一個頁面業務非常複雜,不要把所有邏輯都寫在一個ViewModel,可以把頁面做業務劃分,把不同的業務放到不同的ViewModel,然後整合到一個總的ViewModel,這樣做起來可以使我們的代碼業務清晰,簡短意賅,也方便後人的維護

所以我們是推薦按業務邏輯拆分幾個不同VM來做,而不僅僅是代碼多不多的才做(業務迭代過程中你能保證你們的PM會讓這個頁面的代碼少嗎?)

最後推薦今天剛整理出來的一個篇關於Android MVVM的博客 如何構建Android MVVM應用程序

以上的內容均出自這篇博文,一些內容是自己個人的理解和總結,如有不對的地方歡迎留言,共同探討。


@M.A.G.I 已經說的很好拉,我來補充一些自己的看法吧~

事實上 Activity、Fragment 太「重」是促使 MVP、MVVM 開發框架出現的一個原因,那其他原因是啥呢?不妨看看 Google 推薦所有開發者參考的開發模板 App —— IOsched(應該沒打錯……),IOsched 中的代碼架構就是當時 Google 認為最值得我們參照的開發模板,然而這種開發架構並沒有什麼軟用……為啥呢?

1、例如 IOsched 中一個很簡單很簡單的功能:點擊 + 號按鈕添加 Google IO 到手機的日曆中,我們現在要為它實現測試單元。然後你一番折騰,整個人都快要爆炸了,測試單元還是沒能弄出來。為啥呢?是功夫不到家嗎?某種程度上你可以這樣覺得,但這個鍋並不能給你。根源在於,很多情況下你要在 Android 的 Activity、Fragment 這些類里實現測試單元是不可能的,因為你無法滿足進行單元測試的三個條件。

2、除此以外,雖然 Google 極力推薦我們使用 Fragment,但是 Fragment 一點也不好用,Fragment 的蛋疼之處就在於它那複雜的一B的生命周期。NB 如 Square 都只能憤怒地棄用 Fragment,自己弄了一套框架替代 Fragment。

以上兩點我在博客都有相關的博文哈

追風箏的吃貨,汪~


MVVM是非常好的一套設計模式,看了問題才知道Android也引入了這個模式,回家了看一下。之前一直用WPF和MVVM。業務邏輯放在Model中,VM負責調用Model,所以不用擔心VM會太大。


今年的Google IO 大會上,Android 團隊發布了一個數據綁定框架(Data Binding Library)。以後可以直接在 layout 布局 xml 文件中綁定數據了,無需再 findViewById 然後手工設置數據了。其語法和使用方式和 JSP 中的 EL 表達式非常類似。下面就來介紹怎麼使用Data Binding Library。
配置環境

目前,最新版的Android Studio已經內置了該框架的支持,配置起來也很簡單,只需要編輯app目錄下的build.gradle文件,添加下面的內容就好了

android {
....
dataBinding {
enabled = true
}
}

Data Binding Layout文件

Data Binding layout文件有點不同的是:起始根標籤是 layout,接下來一個 data 元素以及一個 view 的根元素。這個 view 元素就是你沒有使用Data Binding的layout文件的根元素。舉例說明如下:

&
http://schemas.android.com/apk/res/android">
&
&
&

&
&
&
&
&

上面定義了一個com.example.User類型的變數user,然後接著android:text="@{user.firstName}"把變數user的firstName屬性的值和TextView的text屬性綁定起來。


Data Object

我們來看下上面用到的com.example.User對象。

public class User {
public final String firstName;
public final String lastName;
public User(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
}

他有兩個public的屬性firstName,lastName,這和上面layout文件裡面的@{user.firstName}和@{user.lastName}對應

或者下面這種形式的對象也是支持的。

public class User {
private final String firstName;
private final String lastName;
public User(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
// getXXX形式
public String getFirstName() {
return this.firstName;
}
// 或者屬性名和方法名相同
public String lastName() {
return this.lastName;
}
}

綁定數據

添加完&標籤後,Android Studio就會根據xml的文件名自動生成一個繼承ViewDataBinding的類。例如: activity_main.xml就會生成ActivityMainBinding, 然後我們在Activity裡面添加如下代碼:

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
MainActivityBinding binding = DataBindingUtil.setContentView(this, R.layout.main_activity);
User user = new User("Test", "User");
binding.setUser(user);
}

綁定事件

就像你可以在xml文件裡面使用屬性android:onClick綁定Activity裡面的一個方法一樣,Data Binding Library擴展了更多的事件可以用來綁定方法,比如View.OnLongClickListener有個方法onLongClick(), 你就可以使用android:onLongClick屬性來綁定一個方法,需要注意的是綁定的方法的簽名必須和該屬性原本對應的方法的簽名完全一樣,否則編譯階段會報錯。

下面舉例來說明具體怎麼使用,先看用來綁定事件的類:

public class MyHandlers {
public void onClickButton(View view) { ... }
public void afterFirstNameChanged(Editable s) { ... }
}

然後就是layout文件:

&
http://schemas.android.com/apk/res/android">
&
&
&
&

&
&
&

一份理想的移動應用市場的分析報告,應該包含哪些內容?
國內 Android 應用生態中的有哪些黑產?
OpenGL ES 對 DirectX?
雙核1.7GHz 四核1.2GHz 哪個更強?
Android 和 Chrome OS 會最終合併成一個平台嗎?如何合併?

TAG:Android開發 | Android | MVVM |