初探安卓MVVM框架設(shè)計(jì)

初探安卓MVVM框架設(shè)計(jì)#

一. 什么是MVVM?

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

1. MVC模式

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

視圖(View):用戶界面

控制器(Controller):業(yè)務(wù)邏輯

模型(Model):數(shù)據(jù)保存

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


1.View傳送指令到Controller

2.Controller完成業(yè)務(wù)邏輯后,要求Model改變狀態(tài)

3.Model將新的數(shù)據(jù)發(fā)送到View,用戶得到反饋

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

Model層: sqlite數(shù)據(jù)庫(kù), JavaBean, SharedPreference, sdcard,獲取網(wǎng)絡(luò)數(shù)據(jù)的api等
View層: xml布局文件,自定義控件等

Controller層: Activity等
此處需要注意的是,在傳統(tǒng)的MVC設(shè)計(jì)模式中,

Activity屬于Controller層而不是View層,因?yàn)锳ctivity即承擔(dān)了數(shù)據(jù)調(diào)用,也承擔(dān)了界面展示,相當(dāng)于View和Model中間的協(xié)調(diào)器.很多初學(xué)者都會(huì)誤認(rèn)為Activity屬于View層.當(dāng)然,這種說法僅限用MVC模式,換做其他模式就不一定了哦!

2. MVP模式

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

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

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

2.View與Model不發(fā)生聯(lián)系,都通過Presenter傳遞.

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

當(dāng)這樣調(diào)整了之后, Activity就純粹屬于View層了,所有業(yè)務(wù)邏輯全由Presenter來完成.當(dāng)View界面被用戶操作時(shí)(比如按鈕點(diǎn)擊), View層就會(huì)調(diào)用Presenter完成相關(guān)業(yè)務(wù)邏輯,而Presenter完成了之后,就會(huì)將結(jié)果以回調(diào)的形式傳遞給View層,由View層完成界面刷新.具體代碼如何實(shí)現(xiàn)我就不多說了,因?yàn)槲覀兘裉斓闹攸c(diǎn)是MVVM,如果有興趣研究的話可以在網(wǎng)上搜索MVP相關(guān)的例子程序,我也找了一個(gè),僅供參考:
http://blog.csdn.net/vector_yi/article/details/24719873

3. MVVM模式

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

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


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

有沒有注意到, MVVM和MVP幾乎是一樣的,唯一的不同就在于View和ViewModel之間的那根線, MVP是兩根,表示View調(diào)用Presenter執(zhí)行邏輯,Presenter調(diào)用View來返回?cái)?shù)據(jù),更新界面;MVVM中只有一根線兩個(gè)箭頭,代表的是View和ViewModel雙向綁定,自動(dòng)同步數(shù)據(jù),無需手動(dòng)調(diào)用相關(guān)方法進(jìn)行通信,從而減少了代碼量.而這種雙向綁定的機(jī)制,都?xì)w功于谷歌推出的DataBinding的新功能.下面我們來研究一下到底什么是DataBinding.

二. 使用DataBinding構(gòu)建MVVM框架

2.1 什么是DataBinding

2015 Google IO大會(huì)帶來的DataBinding庫(kù)使得Android開發(fā)者可以方便的實(shí)現(xiàn)MVVM架構(gòu)模式.使用DataBinding可以改善應(yīng)用程序的開發(fā),使代碼更加干凈優(yōu)雅.

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

2.2 DataBinding環(huán)境配置

1.由于新版Android Studio已經(jīng)內(nèi)置了DataBinding的功能,為了方便開發(fā),請(qǐng)確保使用AndroidStudio 1.3及以上的版本.

2.在app的build.gradle文件中添加下面的內(nèi)容:

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

3.重新編譯項(xiàng)目,配置完成.

2.3 DataBinding的基本使用

1.布局文件

根標(biāo)簽使用layout,在layout標(biāo)簽下用data標(biāo)簽來配置數(shù)據(jù),例子如下:

<?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"/>

這句話代表,聲明了一個(gè)user變量,類型是cn.itcast.mvvmdemo.User,當(dāng)然這個(gè)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}"/>

控件布局寫法和以前一樣,唯一不同之處在于控件內(nèi)容的賦值部分.以前我們都會(huì)寫一個(gè)默認(rèn)值,然后再在代碼中動(dòng)態(tài)修改控件的值.此時(shí)已經(jīng)不需要了. @{user.firstname}代表當(dāng)前TextView的值取自于user對(duì)象中的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自動(dòng)根據(jù)布局文件生成的類,不需要手動(dòng)創(chuàng)建.該類的命名方式取自于布局文件的名稱.比如布局文件名叫activity_main,那么生成的類名就叫ActivityMainBinding.

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

3. 運(yùn)行效果

你會(huì)發(fā)現(xiàn),我們沒有像往常那樣在activity中findViewById,找到控件后給動(dòng)態(tài)賦值,而是通過DataBinding的方式直接將對(duì)象的值作用在了布局文件中,從而使我們的代碼更加優(yōu)雅和簡(jiǎn)潔.

2.3 DataBinding響應(yīng)點(diǎn)擊事件

1.首先,寫一個(gè)事件處理器MyHandler

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

這是一個(gè)普通的類,在onButtonClick中處理按鈕點(diǎn)擊后應(yīng)該執(zhí)行的操作.

2.在之前布局文件的基礎(chǔ)上,添加一個(gè)按鈕

<?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="點(diǎn)擊我"
            />
    </LinearLayout>
</layout>

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

3.在Activity中,將MyHandler設(shè)置給Binding對(duì)象.

binding.setHandler(new MyHandler());

4.運(yùn)行后看效果

2.4 數(shù)據(jù)變化后同步更新界面

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

1.將用戶對(duì)象傳遞給MyHandler

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

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

2.我們需要把User類調(diào)整一下:

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兩個(gè)方法中加注解@Bindable,這樣的話DataBinding會(huì)自動(dòng)在BR文件中生成這兩個(gè)字段的id. BR文件類似于R文件,是DataBinding特有的用于維護(hù)id的一個(gè)文件. BR文件由編譯器自動(dòng)生成.

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

3.我們案例的最終項(xiàng)目結(jié)構(gòu)如下圖所示:

三. 總結(jié)

在學(xué)習(xí)MVVM框架時(shí)我一直有一個(gè)糾結(jié):MVC和MVP結(jié)構(gòu)很清晰,我很容易能分清楚哪個(gè)組件屬于哪個(gè)模塊,但到了MVVM我就有點(diǎn)暈了,因?yàn)榫W(wǎng)上所有介紹MVVM的文章幾乎都指向了DataBinding,并沒有講到具體每一層對(duì)應(yīng)哪些組件.目前就我的初步了解,我大概會(huì)這么劃分:

View層: xml, Activity,自定義控件等;
Model層: sqlite數(shù)據(jù)庫(kù), JavaBean,SharedPreference, sdcard,獲取網(wǎng)絡(luò)數(shù)據(jù)的api等
ViewModel層:獨(dú)立的業(yè)務(wù)邏輯處理模塊,部分參與業(yè)務(wù)邏輯的JavaBean

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

不過后來我又想了一下,我們真有必要?jiǎng)澐智宄l是View,誰是ViewModel,誰是Model嗎?程序設(shè)計(jì)本來就很復(fù)雜,難免會(huì)碰到一些模棱兩可的模塊,各個(gè)層都參與一下,但又不屬于任何一層.我們開發(fā)應(yīng)用程序是為了實(shí)現(xiàn)功能,我們進(jìn)行框架設(shè)計(jì)是為了提高擴(kuò)展性并降低維護(hù)成本,在這種大前提下,我們的細(xì)節(jié)如何處理就已經(jīng)無關(guān)緊要了.事實(shí)上,當(dāng)你采用了DataBinding來構(gòu)建你的程序時(shí),你其實(shí)就已經(jīng)在用MVVM框架了.

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

關(guān)于MVVM和DataBinding的資料和博客,網(wǎng)上已經(jīng)有很多了,由于MVVM內(nèi)容確實(shí)繁雜,所以網(wǎng)上的文章沒有特別全面的,側(cè)重點(diǎn)都有所不同.當(dāng)然,此文是從另一個(gè)角度來重新解讀了一下MVVM模式,如果能從此文中獲取對(duì)你有益的內(nèi)容,會(huì)讓我倍感欣慰.

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 179,366評(píng)論 25 708
  • Android App的設(shè)計(jì)架構(gòu):MVC,MVP,MVVM與架構(gòu)經(jīng)驗(yàn)談1. 架構(gòu)設(shè)計(jì)的目的1.1 通過設(shè)計(jì)使程序模...
    天空在微笑閱讀 4,309評(píng)論 1 20
  • 1、概述 Databinding 是一種框架,MVVM是一種模式,兩者的概念是不一樣的。我的理解DataBindi...
    Kelin閱讀 77,176評(píng)論 68 520
  • 生活里食品有保質(zhì)期,藥品有有效期,電器有保修期,房子有產(chǎn)權(quán)期,所有物質(zhì)的東西都有有效期。那么父親母親的血液澆灌出的...
    雨雪菲閱讀 699評(píng)論 4 4
  • 感受晴天一樣的你 日出即明朗,日落即安詳 有時(shí)閑階小駐足,有時(shí)月色滿瀟湘 品味晴天一樣的你 白云亦舒卷,樹影亦婆娑...
    雨歇夢(mèng)微涼閱讀 263評(píng)論 0 1

友情鏈接更多精彩內(nèi)容