1. 程式人生 > >Android開發模式之MVC,MVP和MVVM的簡單介紹與區別

Android開發模式之MVC,MVP和MVVM的簡單介紹與區別

相信大家對MVC,MVP和MVVM都不陌生,作為三個最耳熟能詳的Android框架,它們的應用可以是非常廣泛的,但是對於一些新手來說,可能對於區分它們三個都有困難,更別說在實際的專案中應用了,有些時候想用MVP的,程式碼寫著寫著就變成了MVC,久而久之就對它們三個的選擇產生了恐懼感,如果你也是這樣的人群,那麼這篇文章可能會對你有很大的幫助,希望大家看完都會有收穫吧!

文章重點:

(1)瞭解並區分MVC,MVP,MVVM。

(2)知道這三種模式在Android中如何使用。

(3)走出data binding的誤區。

(4)瞭解MVP+data binding的開發模式。

本篇文章的demo我將會上傳到我的github上

水之積也不厚,則其負大舟也無力

正如莊子在逍遙遊中說的,如果水不夠深,那就沒有能夠擔負大船的力量 。所以在真正開始涉及具體的程式碼之前,我們要先對MVC,MVP和MVVM做一個初步的瞭解。如果各位同學對此已經有所瞭解了,可以選擇性跳過這一節。

MVC

MVC,Model View Controller,是軟體架構中最常見的一種框架,簡單來說就是通過controller的控制去操作model層的資料,並且返回給view層展示,具體見下圖

mvc

當用戶出發事件的時候,view層會發送指令到controller層,接著controller去通知model層更新資料,model層更新完資料以後直接顯示在view層上,這就是MVC的工作原理。

那具體到Android上是怎麼樣一個情況呢?

大家都知道一個Android工程有什麼對吧,有Java的class檔案,有res資料夾,裡面是各種資源,還有類似manifest檔案等等。對於原生的Android專案來說,layout.xml裡面的xml檔案就對應於MVC的view層,裡面都是一些view的佈局程式碼,而各種Java bean,還有一些類似repository類就對應於model層,至於controller層嘛,當然就是各種activity咯。大家可以試著套用我上面說的MVC的工作原理是理解。比如你的介面有一個按鈕,按下這個按鈕去網路上下載一個檔案,這個按鈕是view層的,是使用xml來寫的,而那些和網路連線相關的程式碼寫在其他類裡,比如你可以寫一個專門的networkHelper類,這個就是model層,那怎麼連線這兩層呢?是通過button.setOnClickListener()這個函式,這個函式就寫在了activity中,對應於controller層。是不是很清晰。

大家想過這樣會有什麼問題嗎?顯然是有的,不然為什麼會有MVP和MVVM的誕生呢,是吧。問題就在於xml作為view層,控制能力實在太弱了,你想去動態的改變一個頁面的背景,或者動態的隱藏/顯示一個按鈕,這些都沒辦法在xml中做,只能把程式碼寫在activity中,造成了activity既是controller層,又是view層的這樣一個窘境。大家回想一下自己寫的程式碼,如果是一個邏輯很複雜的頁面,activity或者fragment是不是動輒上千行呢?這樣不僅寫起來麻煩,維護起來更是噩夢。(當然看過Android原始碼的同學其實會發現上千行的程式碼不算啥,一個RecyclerView.class的程式碼都快上萬行了呢。。)

MVC還有一個重要的缺陷,大家看上面那幅圖,view層和model層是相互可知的,這意味著兩層之間存在耦合,耦合對於一個大型程式來說是非常致命的,因為這表示開發,測試,維護都需要花大量的精力。

正因為MVC有這樣那樣的缺點,所以才演化出了MVP和MVVM這兩種框架。

MVP

MVP作為MVC的演化,解決了MVC不少的缺點,對於Android來說,MVP的model層相對於MVC是一樣的,而activity和fragment不再是controller層,而是純粹的view層,所有關於使用者事件的轉發全部交由presenter層處理。下面還是讓我們看圖

mvp

從圖中就可以看出,最明顯的差別就是view層和model層不再相互可知,完全的解耦,取而代之的presenter層充當了橋樑的作用,用於操作view層發出的事件傳遞到presenter層中,presenter層去操作model層,並且將資料返回給view層,整個過程中view層和model層完全沒有聯絡。看到這裡大家可能會問,雖然view層和model層解耦了,但是view層和presenter層不是耦合在一起了嗎?其實不是的,對於view層和presenter層的通訊,我們是可以通過介面實現的,具體的意思就是說我們的activity,fragment可以去實現實現定義好的介面,而在對應的presenter中通過介面呼叫方法。不僅如此,我們還可以編寫測試用的View,模擬使用者的各種操作,從而實現對Presenter的測試。這就解決了MVC模式中測試,維護難的問題。

當然,其實最好的方式是使用fragment作為view層,而activity則是用於建立view層(fragment)和presenter層(presenter)的一個控制器。

MVVM

MVVM最早是由微軟提出的

mvvm

這裡要感謝泡在網上的日子,因為前面看到的三張圖我都是從它的部落格中摘取的,如果有人知道不允許這樣做的話請告訴我,我會從我的部落格中刪除的,謝謝。

從圖中看出,它和MVP的區別貌似不大,只不過是presenter層換成了viewmodel層,還有一點就是view層和viewmodel層是相互繫結的關係,這意味著當你更新viewmodel層的資料的時候,view層會相應的變動ui。

我們很難去說MVP和MVVM這兩個MVC的變種孰優孰劣,還是要具體情況具體分析。

紙上得來終覺淺,絕知此事要躬行

對於程式設計師來說,空談是最沒效率的一種方式,相信大家看了我上面對於三種模式的分析,或多或少都會有點雲裡霧裡,下面讓我們結合程式碼來看看。

讓我們試想一下下面這個情景,使用者點選一個按鈕A,獲取github上對應公司對應倉庫中貢獻排行第一的任的名字,然後我們還會有一個按鈕B,使用者點選按鈕B,介面上排行第一的那個人的名字就會換成自己的。

MVC

MVC實現是最簡單的。

首先看對應view層的xml檔案

123456789101112131415161718192021222324252627282930<?xml version="1.0" encoding="utf-8"?>android:layout_width="match_parent"android:layout_height="match_parent"android:id="@+id/container"android:orientation="vertical"tools:context=".ui.view.MainActivity"android:fitsSystemWindows="true"><Buttonandroid:text="get"android:layout_width="match_parent"android:layout_height="wrap_content"android:onClick="get"/><Buttonandroid:text="change"android:layout_width="match_parent"android:layout_height="wrap_content"android:onClick="change"/><TextViewandroid:id="@+id/top_contributor"android:layout_width="match_parent"android:layout_height="match_parent"android:gravity="center"android:textSize="30sp"/></LinearLayout>

很簡單,兩個Button一個TextView

接著看對應controller層的activity

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182public class MainActivity extends AppCompatActivity {private ProcessDialog dialog;private Contributor contributor = new Contributor();private TextView topContributor;private Subscriber<Contributor> contributorSub = new Subscriber<Contributor>() {@Overridepublic void onStart() {showProgress();}@Overridepublic void onCompleted() {}@Overridepublic void onError(Throwable e) {}@Overridepublic void onNext(Contributor contributor) {MainActivity.this.contributor = contributor;topContributor.setText(contributor.login);dismissProgress();}};@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);topContributor = (TextView)findViewById(R.id.top_contributor);}public void get(View view){getTopContributor("square""retrofit");}public void change(View view){contributor.login = "zjutkz";topContributor.setText(contributor.login);}public void getTopContributor(String owner,String repo){GitHubApi.getContributors(owner, repo).take(1).observeOn(AndroidSchedulers.mainThread()).subscribeOn(Schedulers.newThread()).map(new Func1<List<Contributor>, Contributor>() {@Overridepublic Contributor call(List<Contributor> contributors) {return contributors.get(0);}}).subscribe(contributorSub);}public void showProgress(){if(dialog == null){dialog = new ProcessDialog(this);}dialog.showMessage("正在載入...");}public void dismissProgress(){if(dialog == null){dialog = new ProcessDialog(this);}dialog.dismiss();}}

我們看一下get()方法中呼叫的getTopContributor方法

1234567891011121314public void getTopContributor(String owner,String repo){GitHubApi.getContributors(owner, repo).take(1).observeOn(AndroidSchedulers.mainThread()).subscribeOn(Schedulers.newThread()).map(new Func1<List<Contributor>, Contributor>() {@Overridepublic Contributor call(List<Contributor> contributors) {return contributors.get(0);}}).subscribe(contributorSub);}

熟悉rxjava和retrofit的同學應該都明白這是啥意思,如果對這兩個開源庫不熟悉也沒事,可以參考給 Android 開發者的 RxJava 詳解用 Retrofit 2 簡化 HTTP 請求這兩篇文章。

對於這裡大家只要知道這段程式碼的意思就是去獲取github上owner公司中的repo倉庫裡貢獻排名第一的那個人。貢獻者是通過Contributor這個java bean儲存的。

12345678910public class Contributor {public String login;public int contributions;@Overridepublic String toString() {return login + ", " + contributions;}}

很簡單,login表示貢獻者的名字,contributor表示貢獻的次數。

然後通過rxjava的subscriber中的onNext()函式得到這個資料。

1234567891011121314151617181920212223242526private Subscriber<Contributor> contributorSub = new Subscriber<Contributor>() {@Overridepublic void onStart() {showProgress();}@Overridepublic void onCompleted() {}@Overridepublic void onError(Throwable e) {}@Overridepublic void onNext(Contributor contributor) {MainActivity.this.contributor = contributor;topContributor.setText(contributor.login);dismissProgress();}};

至於另外那個change按鈕的工作大家應該都看得懂,這裡不重複了。

好了,我們來回顧一遍整個流程。

首先在xml中寫好佈局程式碼。

其次,activity作為一個controller,裡面的邏輯是監聽使用者點選按鈕並作出相應的操作。比如針對get按鈕,做的工作就是呼叫GithubApi的方法去獲取資料。

GithubApi,Contributor等類則表示MVC中的model層,裡面是資料和一些具體的邏輯操作。

說完了流程再來看看問題,還記得我們前面說的嗎,MVC在Android上的應用,一個具體的問題就是activity的責任過重,既是controller又是view。這裡是怎麼體現的呢?看了程式碼大家發現其中有一個progressDialog,在載入資料的時候顯示,載入完了以後取消,邏輯其實是view層的邏輯,但是這個我們沒辦法寫到xml裡面啊,包括TextView.setTextView(),這個也一樣。我們只能把這些邏輯寫到activity中,這就造成了activity的臃腫,這個例子可能還好,如果是一個複雜的頁面呢?大家自己想象一下。

MVP

通過具體的程式碼大家知道了MVC在Android上是如何工作的,也知道了它的缺點,那MVP是如何修正的呢?

這裡先向大家推薦github上的一個第三方庫,通過這個庫大家可以很輕鬆的實現MVP。好了,還是看程式碼吧。

首先還是xml

123456789101112131415161718192021222324252627282930<?xml version="1.0" encoding="utf-8"?>android:layout_width="match_parent"android:layout_height="match_parent"android:id="@+id/container"android:orientation="vertical"tools:context=".ui.view.MainActivity"android:fitsSystemWindows="true"><Buttonandroid:text="get"android:layout_width="match_parent"android:layout_height="wrap_content"android:onClick="get"/><Buttonandroid:text="change"android:layout_width="match_parent"android:layout_height="wrap_content"android:onClick="change"/><TextViewandroid:id="@+id/top_contributor"android:layout_width="match_parent"android:layout_height="match_parent"android:gravity="center"android:textSize="30sp"/></LinearLayout>

這個和MVC是一樣的,畢竟介面的形式是一樣的嘛。

接下去,我們看一個介面。

12345678public interface ContributorView extends MvpView {void onLoadContributorStart();void onLoadContributorComplete(Contributor topContributor);void onChangeContributorName(String name);}

這個介面起什麼作用呢?還記得我之前說的嗎?MVP模式中,view層和presenter層靠的就是介面進行連線,而具體的就是上面的這個了,裡面定義的三個方法,第一個是開始獲取資料,第二個是獲取資料成功,第三個是改名。我們的view層(activity)只要實現這個介面就可以了。

下面看activity的程式碼

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061public class MainActivity extends MvpActivity<ContributorView,ContributorPresenter> implements ContributorView {private ProcessDialog dialog;private TextView topContributor;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);topContributor = (TextView)findViewById(R.id.top_contributor);}@NonNull@Overridepublic ContributorPresenter createPresenter() {return new ContributorPresenter();}public void get(View view){getPresenter().get("square""retrofit");}public void change(View view){getPresenter().change();}@Overridepublic void onLoadContributorStart() {showProgress();}@Overridepublic void onLoadContributorComplete(Contributor contributor) {topContributor.setText(contributor.toString());dismissProgress();}@Overridepublic void onChangeContributorName(String name) {topContributor.setText(name);}public void showProgress(){if(dialog == null){dialog = new ProcessDialog(this);}dialog.showMessage("正在載入...");}public void dismissProgress(){if(dialog == null){dialog = new ProcessDialog(this);}dialog.dismiss();}}

它繼承自MvpActivity,實現了剛才的ContributorView介面。繼承的那個MvpActivity大家這裡不用太關心主要是做了一些初始化和生命週期的封裝。我們只要關心這個activity作為view層,到底是怎麼工作的。

1234567public void get(View view){getPresenter().get("square""retrofit");}public void change(View view){getPresenter().change();}

get()和change()這兩個方法是我們點選按鈕以後執行的,可以看到,裡面完完全全沒有任何和model層邏輯相關的東西,只是簡單的委託給了presenter,那我們再看看presenter層做了什麼

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253public class ContributorPresenter extends MvpBasePresenter<ContributorView> {private Subscriber<Contributor> contributorSub = new Subscriber<Contributor>() {@Overridepublic void onStart() {ContributorView view = getView();if(view != null){view.onLoadContributorStart();}}@Overridepublic void onCompleted() {}@Overridepublic void onError(Throwable e) {}@Overridepublic void onNext(Contributor topContributor) {ContributorView view = getView();if(view != null){view.onLoadContributorComplete(topContributor);}}};public void get(String owner,String repo){GitHubApi.getContributors(owner, repo).take(1).observeOn(AndroidSchedulers.mainThread()).subscribeOn(Schedulers.newThread()).map(new Func1<List<Contributor>, Contributor>() {@Overridepublic Contributor call(List<Contributor> contributors) {return contributors.get(0);}}).subscribe(contributorSub);}public void change(){ContributorView view = getView();if(view != null){view.onChangeContributorName("zjutkz");}}}

其實就是把剛才MVC中activity的那部分和model層相關的邏輯抽取了出來,並且在相應的時機呼叫ContributorView介面對應的方法,而我們的activity是實現了這個介面的,自然會走到對應的方法中。

好了,我們來捋一捋。

首先,和MVC最大的不同,MVP把activity作為了view層,通過程式碼也可以看到,整個activity沒有任何和model層相關的邏輯程式碼,取而代之的是把程式碼放到了presenter層中,presenter獲取了model層的資料之後,通過介面的形式將view層需要的資料返回給它就OK了。

這樣的好處是什麼呢?首先,activity的程式碼邏輯減少了,其次,view層和model層完全解耦,具體來說,如果你需要測試一個http請求是否順利,你不需要寫一個activity,只需要寫一個java類,實現對應的介面,presenter獲取了資料自然會呼叫相應的方法,相應的,你也可以自己在presenter中mock資料,分發給view層,用來測試佈局是否正確。

MVVM

首先在看這段內容之前,你需要保證你對data binding框架有基礎的瞭解。不瞭解的同學可以去看下這篇文章。在接下去讓我們開始探索MVVM,MVVM最近在Android上可謂十分之火,最主要的原因就是谷歌推出了data binding這個框架,可以輕鬆的實現MVVM。但是,我在網上查閱關於Android的data binding資料的時候,發現國內有很多人都誤解了,首先,我們從一篇錯誤的文章開始。當然我在這裡引用這篇文章也是對事不對人,如果對文章的作者產生了不好的影響我這裡說一聲抱歉。

上面那篇文章是一個關於data binding的使用,看起來很美好,但是,其中有一個錯誤可以說是非常,非常,非常嚴重的。

blob.png

它竟然說data binding的viewmodel層是binding類,其實不止是這篇文章,其他有一些開發者寫的關於data binding的文章裡都犯了一樣的錯誤。大家如果也有這樣的概念,請務必糾正過來!!

說完了錯誤的概念,那data binding中真正的viewmodel是什麼呢?我們還是以之前MVC,MVP的那個例子做引導。

首先是view層,這沒啥好說的,和MVP一樣,view層就是xml和activity。

123456789101112131415161718192021222324252627282930<?xml version="1.0" encoding="utf-8"?>android:layout_width="match_parent"android:layout_height="match_parent"android:id="@+id/container"android:orientation="vertical"tools:context=".ui.view.MainActivity"android:fitsSystemWindows="true"><Buttonandroid:text="get"android:layout_width="match_parent"android:layout_height="wrap_content"android:onClick="get"/><Buttonandroid:text="change"android:layout_width="match_parent"android:layout_height="wrap_content"android:onClick="change"/><TextViewandroid:id="@+id/top_contributor"android:layout_width="match_parent"android:layout_height="match_parent"android:gravity=

相關推薦

Android開發模式MVCMVPMVVM簡單介紹區別

相信大家對MVC,MVP和MVVM都不陌生,作為三個最耳熟能詳的Android框架,它們的應用可以是非常廣泛的,但是對於一些新手來說,可能對於區分它們三個都有困難,更別說在實際的專案中應用了,有些時候想用MVP的,程式碼寫著寫著就變成了MVC,久而久之就對它們三個的選擇產生了

Android App的三種架構模式MVCMVPMVVM

http://www.2cto.com/kf/201506/405766.html MVC使用總結 利用MVC設計模式,使得這個天氣預報小專案有了很好的可擴充套件和維護性,當需要改變UI顯示的時候,無需修改Contronller(控制器)Activity的程式

androidMVCMVPMVVM三種模式詳解析

我們都知道,android本身就採用了MVC模式,model層資料來源層我們就不說了,至於view層即通過xml來體現,而 controller層的角色一般是由activity來擔當的。雖然我們專案用到了MVP模式,但是現在人們並沒有總結出一種規範,所以MVP模式的寫法並不

教你認清MVCMVPMVVM三種模式

相信大家對MVC,MVP和MVVM都不陌生,作為三個最耳熟能詳的Android框架,它們的應用可以是非常廣泛的,但是對於一些新手來說,可能對於區分它們三個都有困難,更別說在實際的專案中應用了,有些時候想用MVP的,程式碼寫著寫著就變成了MVC,久而久之就對它們三

設計模式-MVCMVP MVVM

複雜的軟體必須有清晰合理的架構,否則無法開發和維護。 MVC(Model-View-Controller)是最常見的軟體架構之一,業界有著廣泛應用。它本身很容易理解,但是要講清楚它與衍生的 MVP 和 MVVM 架構的區別就不容易了。 昨天晚上,我讀了《Scali

認清Android框架 MVCMVPMVVM

(2)知道這三種模式在Android中如何使用。 (3)走出data binding的誤區。 (4)瞭解MVP+data binding的開發模式。 本篇文章的demo我將會上傳到我的github上。 水之積也不厚,則其負大舟也無力 正如莊子在逍遙遊中說的,如果水不夠深,那就沒有能夠擔負大船的

MVCMVP MVVM

指令 hang mvc route text nts -i fonts ng- MVC,MVP 和 MVVM 的圖示 MVC(Model-View-Controller)是最常見的軟件架構之一,業界有著廣泛應用。它本身很容易理解,但是要講清楚,它與衍生的 MVP 和 MV

MVCMVP MVVM 的圖示

綁定 則無 作用 com 理解 兩種 區別 維護 保留 http://www.ruanyifeng.com/blog/2015/02/mvcmvp_mvvm.html 復雜的軟件必須有清晰合理的架構,否則無法開發和維護。 MVC(Model-View-Controlle

MVCMVP MVVM 的詳解

name one control ember 模式 hash 改名 主動性 主動 一、MVC MVC模式的意思是,軟件可以分成三個部分。 視圖(View):用戶界面。 控制器(Controller):業務邏輯 模型(Model):數據保存 各部分之間的通信方式如下

MVCMVP MVVM 的圖示區別

mage 否則 nbsp lan ng- 意思 scrip 業界 作用 作者: 阮一峰 日期: 2015年2月 1日 復雜的軟件必須有清晰合理的架構,否則無法開發和維護。 MVC(Model-View-Controller)是最常見的軟件架構之一,業界有著廣泛應用。它本身

MVCMVP MVVM區別

一、MVC MVC模式的意思是,軟體可以分成三個部分。 檢視(View):使用者介面。 控制器(Controller):業務邏輯 模型(Model):資料儲存   1.View 傳送指令到 Controller 2.Controller 完

MVCMVP MVVM(轉載)

1 什麼是MVC MVC的目的是為了把資料(Model)和檢視(View)分離開來,然後用控制器(Controller)作膠水來粘合M和V之間的關係。 這樣做的目的是為了實現注意點分離這樣一個更高層次的設計理念,也就是讓專業的物件做專業的事情,View就只負責檢視相關的東西,Mode

淺談MVCMVP MVVM區別

複雜的軟體必須有清晰合理的架構,否則無法開發和維護。 以下以Javascript客戶端頁面開發為例使用圖示簡單闡述三者的聯絡和區別。 需要注意的是,MVC開發模式備廣泛用於各種軟體開發中,包括網際網路的B/S模式的產品,而其他兩種模式大多數用在客戶端開發中,例如:Javascrtipt、WPF、Adn

細說Android框架設計三劍客MVCMVPMVVM

    最近幾年的移動端開發越來越火,功能越來越強大,處理業務越來越複雜,因此對系統擴充套件性的要求越來越高。而為了更好地進行移動端架構設計,我們最常用的就是MVC和MVP,今天本篇部落格就和大家一起聊一聊這兩種框架設計。 MVC框架 MVC的定義

Android 架構設計:MVCMVPMVVM元件化

MVC、MVP和MVVM是常見的三種架構設計模式,當前MVP和MVVM的使用相對比較廣泛,當然MVC也並沒有過時之說。而所謂的元件化就是指將應用根據業務需求劃分成各個模組來進行開發,每個模組又可以編譯成獨立的APP進行開發。理論上講,元件化和前面三種架構設計不是

Android開發基本控制元件詳解四種佈局方式

Android中的控制元件的使用方式和iOS中控制元件的使用方式基本相同,都是事件驅動。給控制元件新增事件也有介面回撥和委託代理的方式。今天這篇部落格就總結一下Android中常用的基本控制元件以及佈局方式。說到佈局方式Android和iOS還是區別挺大的,在iOS中有Frame絕對佈局和AutoL

Android框架MVCMVPMVVM探究(圖解+案例+附原始碼)

1、介紹 MVC、MVP、MVVM這些模式是為了解決開發過程中的實際問題而提出來的,目前作為主流的幾種架構模式而被廣泛使用。本文程式碼 2、瞭解並區分MVC,MVP,MVVM 2.1 MVC MVC,(Model View Controller)

[Android] Android開發優化——使用軟引用弱引用

Java從JDK1.2版本開始,就把物件的引用分為四種級別,從而使程式能更加靈活的控制物件的生命週期。這四種級別由高到低依次為:強引用、軟引用、弱引用和虛引用。這裡重點介紹一下軟引用和弱引用。如果一個物件只具有軟引用,那麼如果記憶體空間足夠,垃圾回收器就不會回收它;如果記憶體

Android 開發技巧 禁止截圖螢幕常亮

1 禁止截圖         出於安全考慮,我們需要對某些介面,比如二維碼介面,登入介面禁止截圖。(像支付寶的付款二維碼)   禁止截圖的程式碼很簡單就一行,在Activity 生命初期的onCre

AndroidMVCMVPMVVM(Data Binding)

本文旨在記錄最近總結的Android客戶端架構,作為學習筆記,歡迎批評指正。 一、概述 MVC:Model - View - Controller MVP: Model - View - Presenter MVVC:Model - View - V