初探安卓MVVM框架設計

初探安卓MVVM框架設計#

一. 什么是MVVM?

MVVM是近幾年流行的一種設計框架,基于該框架設計的應用程序具有良好的解耦和可擴展性,大幅降低了維護成本,提高了程序員的開發效率.在了解MVVM框架之前,我們有必要回顧一下其他設計框架.

1. MVC模式

MVC模式的意思是,軟件可以分成三個部分.

視圖(View):用戶界面

控制器(Controller):業務邏輯

模型(Model):數據保存

各部分之間的通信方式如下.


1.View傳送指令到Controller

2.Controller完成業務邏輯后,要求Model改變狀態

3.Model將新的數據發送到View,用戶得到反饋

所有通信都是單向的.我們傳統的Android開發都是基于這種模式.每一層可以代表我們常用的如下組件:

Model層: sqlite數據庫, JavaBean, SharedPreference, sdcard,獲取網絡數據的api等
View層: xml布局文件,自定義控件等

Controller層: Activity等
此處需要注意的是,在傳統的MVC設計模式中,

Activity屬于Controller層而不是View層,因為Activity即承擔了數據調用,也承擔了界面展示,相當于View和Model中間的協調器.很多初學者都會誤認為Activity屬于View層.當然,這種說法僅限用MVC模式,換做其他模式就不一定了哦!

2. MVP模式

MVC模式普及了一段時間之后,逐漸暴露出一些問題.比如我們發現,
Activity中寫的代碼太多,有時候一個Activity甚至達到了四五千行代碼,維護起來極為不便.原因也很明顯,就是Activity既參與api訪問和數據調用,又參與了界面的更新,職能劃分不明確,沒有完全實現解耦.我們的想法是,能不能讓Activity只做界面響應和更新,其他業務邏輯全部由另外一個單獨模塊來完成?于是MVP誕生了.

MVP模式將Controller改名為Presenter,同時改變了通信方向.

1.各部分之間的通信,都是雙向的.

2.View與Model不發生聯系,都通過Presenter傳遞.

3.View非常薄,不部署任何業務邏輯,稱為"被動視圖"(Passive View),即沒有任何主動性,而Presenter非常厚,所有邏輯都部署在那里.

當這樣調整了之后, Activity就純粹屬于View層了,所有業務邏輯全由Presenter來完成.當View界面被用戶操作時(比如按鈕點擊), View層就會調用Presenter完成相關業務邏輯,而Presenter完成了之后,就會將結果以回調的形式傳遞給View層,由View層完成界面刷新.具體代碼如何實現我就不多說了,因為我們今天的重點是MVVM,如果有興趣研究的話可以在網上搜索MVP相關的例子程序,我也找了一個,僅供參考:
http://blog.csdn.net/vector_yi/article/details/24719873

3. MVVM模式

當我們采用MVP模式之后,發現Activity幾乎沒啥事可做了,我們的項目代碼層級也清晰了,也好維護了.但是MVP也有缺點,比如,為了實現MVP,我們需要額外增加好多接口和類,比如,一個Activity需要對應一個Presenter類和Presenter接口,同時為了方便Activity和Presenter進行通信,還得再定義一個回調接口IView,也就是說,每一個Activity都需要額外增加兩個接口和一個類,無疑提高了代碼量.而MVVM的誕生,就解決了這個問題!

MVVM模式將Presenter改名為ViewModel,基本上與MVP模式完全一致.


唯一的區別是,它采用雙向綁定(data-binding):View的變動,自動反映在ViewModel,反之亦然.

有沒有注意到, MVVM和MVP幾乎是一樣的,唯一的不同就在于View和ViewModel之間的那根線, MVP是兩根,表示View調用Presenter執行邏輯,Presenter調用View來返回數據,更新界面;MVVM中只有一根線兩個箭頭,代表的是View和ViewModel雙向綁定,自動同步數據,無需手動調用相關方法進行通信,從而減少了代碼量.而這種雙向綁定的機制,都歸功于谷歌推出的DataBinding的新功能.下面我們來研究一下到底什么是DataBinding.

二. 使用DataBinding構建MVVM框架

2.1 什么是DataBinding

2015 Google IO大會帶來的DataBinding庫使得Android開發者可以方便的實現MVVM架構模式.使用DataBinding可以改善應用程序的開發,使代碼更加干凈優雅.

DataBinding的使用教程在網上已經很多了,我在這里只是簡單提一下最基本的用法,大家體驗一下就好.如果想更深入學習的話,建議查看谷歌官方文檔:https://developer.android.com/topic/libraries/data-binding/index.html

2.2 DataBinding環境配置

1.由于新版Android Studio已經內置了DataBinding的功能,為了方便開發,請確保使用AndroidStudio 1.3及以上的版本.

2.在app的build.gradle文件中添加下面的內容:

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

3.重新編譯項目,配置完成.

2.3 DataBinding的基本使用

1.布局文件

根標簽使用layout,在layout標簽下用data標簽來配置數據,例子如下:

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
    <data>
        <variable name="user" type="cn.itcast.mvvmdemo.User"/>
    </data>
    <LinearLayout
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <TextView android:layout_width="wrap_content"
                  android:layout_height="wrap_content"
                  android:text="@{user.firstname}"/>
        <TextView android:layout_width="wrap_content"
                  android:layout_height="wrap_content"
                  android:text="@{user.lastname}"/>
    </LinearLayout>
</layout>

<variable name="user" type="cn.itcast.mvvmdemo.User"/>

這句話代表,聲明了一個user變量,類型是cn.itcast.mvvmdemo.User,當然這個User要提前定義.

public class User {
private String firstname;
private String lastname;
public User(String firstname, Stringlastname) {
this.firstname = firstname;
this.lastname = lastname;
}
public String getFirstname() {
return firstname;
}
public void setFirstname(Stringfirstname) {
this.firstname = firstname;
}
public String getLastname() {
return lastname;
}
public void setLastname(Stringlastname) {
this.lastname = lastname;
}
}
<TextView android:layout_width="wrap_content"
                  android:layout_height="wrap_content"
                  android:text="@{user.firstname}"/>

控件布局寫法和以前一樣,唯一不同之處在于控件內容的賦值部分.以前我們都會寫一個默認值,然后再在代碼中動態修改控件的值.此時已經不需要了. @{user.firstname}代表當前TextView的值取自于user對象中的firstname字段.

2. Activity代碼

public class MainActivity extends AppCompatActivity {
private User user;
@Override
protected void onCreate(BundlesavedInstanceState) {
super.onCreate(savedInstanceState);
ActivityMainBinding binding =DataBindingUtil.setContentView(this, R.layout.activity_main);
user = new User("尼古拉斯凱奇", "趙四");
binding.setUser(user);
}
}

ActivityMainBinding是DataBinding自動根據布局文件生成的類,不需要手動創建.該類的命名方式取自于布局文件的名稱.比如布局文件名叫activity_main,那么生成的類名就叫ActivityMainBinding.

當使用DataBinding時,需要用DataBindingUtil來設置Activity的布局.
binding.setUser(user);表示將user對象和布局文件綁定在了一起,
user對象的所有屬性值都可以同步映射到布局文件的控件中.

3. 運行效果

你會發現,我們沒有像往常那樣在activity中findViewById,找到控件后給動態賦值,而是通過DataBinding的方式直接將對象的值作用在了布局文件中,從而使我們的代碼更加優雅和簡潔.

2.3 DataBinding響應點擊事件

1.首先,寫一個事件處理器MyHandler

public class MyHandler {
public void onButtonClick(View view){
System.out.println("按鈕被點擊了");
}
}

這是一個普通的類,在onButtonClick中處理按鈕點擊后應該執行的操作.

2.在之前布局文件的基礎上,添加一個按鈕

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">

    <data>

        <variable
            name="user"
            type="cn.itcast.mvvmdemo.User"/>

        <variable
            name="handler"
            type="cn.itcast.mvvmdemo.MyHandler"/>
    </data>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{user.firstname}"/>

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{user.lastname}"/>

        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:onClick="@{handler.onButtonClick}"
            android:text="點擊我"
            />
    </LinearLayout>
</layout>

在data中聲明handler,類型是MyHandler,在Button的onClick中定義要執行的操作.
android:onClick="@{handler.onButtonClick}"

3.在Activity中,將MyHandler設置給Binding對象.

binding.setHandler(new MyHandler());

4.運行后看效果

2.4 數據變化后同步更新界面

點擊按鈕之后,我們想修改一下firstname和lastname的值,然后更新界面.如果采用DataBinding的話,我們會怎么做?

1.將用戶對象傳遞給MyHandler

public class MyHandler {
private User user;
public MyHandler(User user) {
this.user = user;
}
public void onButtonClick(View view){
System.out.println("按鈕被點擊了");
user.setFirstname("蒙拉麗莎");
user.setLastname("鴨蛋");
}
}

在按鈕點擊的時候,修改了user的firstname和lastname.如果放在往常,你肯定就立馬想找到那兩個TextView對象來重新設置數據,而現在,你什么都不用做,只要數據變了,界面就會立即同步更新.有這么神奇?其實你得提前做好準備,才會有這樣的效果.

2.我們需要把User類調整一下:

public class User extends BaseObservable {
private String firstname;
private String lastname;
public User(String firstname, Stringlastname) {
this.firstname = firstname;
this.lastname = lastname;
}
@Bindable
public String getFirstname() {
return firstname;
}
public void setFirstname(String firstname) {
this.firstname = firstname;
notifyPropertyChanged(BR.firstname);
}
@Bindable
public String getLastname() {
return lastname;
}
public void setLastname(String lastname){
this.lastname = lastname;
notifyPropertyChanged(BR.lastname);
}
}

在getFirstname和getLastname兩個方法中加注解@Bindable,這樣的話DataBinding會自動在BR文件中生成這兩個字段的id. BR文件類似于R文件,是DataBinding特有的用于維護id的一個文件. BR文件由編譯器自動生成.

在setFirstname和setLastname的方法中添加notifyPropertyChanged方法,同時將你要更新的字段id傳遞過去.此方法用于通知系統數據已經變化,需要更新界面.

3.我們案例的最終項目結構如下圖所示:

三. 總結

在學習MVVM框架時我一直有一個糾結:MVC和MVP結構很清晰,我很容易能分清楚哪個組件屬于哪個模塊,但到了MVVM我就有點暈了,因為網上所有介紹MVVM的文章幾乎都指向了DataBinding,并沒有講到具體每一層對應哪些組件.目前就我的初步了解,我大概會這么劃分:

View層: xml, Activity,自定義控件等;
Model層: sqlite數據庫, JavaBean,SharedPreference, sdcard,獲取網絡數據的api等
ViewModel層:獨立的業務邏輯處理模塊,部分參與業務邏輯的JavaBean

在我們的例子項目中,MainActivity, activity_main.xml屬于View層; User屬于Model層; MyHandler屬于ViewModle層.

不過后來我又想了一下,我們真有必要劃分清楚誰是View,誰是ViewModel,誰是Model嗎?程序設計本來就很復雜,難免會碰到一些模棱兩可的模塊,各個層都參與一下,但又不屬于任何一層.我們開發應用程序是為了實現功能,我們進行框架設計是為了提高擴展性并降低維護成本,在這種大前提下,我們的細節如何處理就已經無關緊要了.事實上,當你采用了DataBinding來構建你的程序時,你其實就已經在用MVVM框架了.

當然DataBinding的用法還有很多,此文介紹的只是冰山一角,比如如何在ListView和RecyclerView中使用DataBinding,布局文件中關于DataBinding的高級用法等等,此文都沒有提及.如果你想了解更多,就請關注官方文檔.

關于MVVM和DataBinding的資料和博客,網上已經有很多了,由于MVVM內容確實繁雜,所以網上的文章沒有特別全面的,側重點都有所不同.當然,此文是從另一個角度來重新解讀了一下MVVM模式,如果能從此文中獲取對你有益的內容,會讓我倍感欣慰.

Demo附件下載鏈接: http://pan.baidu.com/s/1pLligyf

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,533評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,055評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,365評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,561評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,346評論 6 404
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,889評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,978評論 3 439
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,118評論 0 286
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,637評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,558評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,739評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,246評論 5 355
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 43,980評論 3 346
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,362評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,619評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,347評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,702評論 2 370

推薦閱讀更多精彩內容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,591評論 25 707
  • Android App的設計架構:MVC,MVP,MVVM與架構經驗談1. 架構設計的目的1.1 通過設計使程序模...
    天空在微笑閱讀 4,158評論 1 20
  • 1、概述 Databinding 是一種框架,MVVM是一種模式,兩者的概念是不一樣的。我的理解DataBindi...
    Kelin閱讀 76,852評論 68 521
  • 生活里食品有保質期,藥品有有效期,電器有保修期,房子有產權期,所有物質的東西都有有效期。那么父親母親的血液澆灌出的...
    雨雪菲閱讀 643評論 4 4
  • 感受晴天一樣的你 日出即明朗,日落即安詳 有時閑階小駐足,有時月色滿瀟湘 品味晴天一樣的你 白云亦舒卷,樹影亦婆娑...
    雨歇夢微涼閱讀 240評論 0 1