MVP是模型(Model)、視圖(View)、主持人(Presenter)的縮寫,分別表明項目中3個不一樣的模塊。html
模型(Model):負責處理數據的加載或者存儲,好比從網絡或本地數據庫獲取數據等;java
視圖(View):負責界面數據的展現,與用戶進行交互;android
主持人(Presenter):至關於協調者,是模型與視圖之間的橋樑,將模型與視圖分離開來。git
以下圖所示,View與Model並不直接交互,而是使用Presenter做爲View與Model之間的橋樑。其中Presenter中同時持有Viwe層以及Model層的Interface的引用,而View層持有Presenter層Interface的引用。當View層某個界面須要展現某些數據的時候,首先會調用Presenter層的某個接口,而後Presenter層會調用Model層請求數據,當Model層數據加載成功以後會調用Presenter層的回調方法通知Presenter層數據加載完畢,最後Presenter層再調用View層的接口將加載後的數據展現給用戶。這就是MVP模式的整個核心過程。github
這樣分層的好處就是大大減小了Model與View層之間的耦合度。一方面可使得View層和Model層單獨開發與測試,互不依賴。另外一方面Model層能夠封裝複用,能夠極大的減小代碼量。固然,MVP還有其餘的一些優勢,這裏再也不贅述。下面看下MVP模式在具體項目中的使用。數據庫
提供咱們想要展現在view層的數據和具體登錄業務邏輯處理的實現,設計模式
1 package com.nsu.edu.androidmvpdemo.login; 2 3 /** 4 * Created by Anthony on 2016/2/15. 5 * Class Note:模擬登錄的操做的接口,實現類爲LoginModelImpl.至關於MVP模式中的Model層 6 */ 7 public interface LoginModel { 8 void login(String username, String password, OnLoginFinishedListener listener); 9 }
1 package com.nsu.edu.androidmvpdemo.login; 2 3 import android.os.Handler; 4 import android.text.TextUtils; 5 /** 6 * Created by Anthony on 2016/2/15. 7 * Class Note:延時模擬登錄(2s),若是名字或者密碼爲空則登錄失敗,不然登錄成功 8 */ 9 public class LoginModelImpl implements LoginModel { 10 11 @Override 12 public void login(final String username, final String password, final OnLoginFinishedListener listener) { 13 14 new Handler().postDelayed(new Runnable() { 15 @Override public void run() { 16 boolean error = false; 17 if (TextUtils.isEmpty(username)){ 18 listener.onUsernameError();//model層裏面回調listener 19 error = true; 20 } 21 if (TextUtils.isEmpty(password)){ 22 listener.onPasswordError(); 23 error = true; 24 } 25 if (!error){ 26 listener.onSuccess(); 27 } 28 } 29 }, 2000); 30 } 31 }
負責顯示數據、提供友好界面跟用戶交互就行。MVP下Activity和Fragment以及View的子類體如今了這一 層,Activity通常也就作加載UI視圖、設置監聽再交由Presenter處理的一些工做,因此也就須要持有相應Presenter的引用。本層所須要作的操做就是在每一次有相應交互的時候,調用presenter的相關方法就行。(好比說,button點擊)網絡
1 package com.nsu.edu.androidmvpdemo.login; 2 3 /** 4 * Created by Anthony on 2016/2/15. 5 * Class Note:登錄View的接口,實現類也就是登錄的activity 6 */ 7 public interface LoginView { 8 void showProgress(); 9 10 void hideProgress(); 11 12 void setUsernameError(); 13 14 void setPasswordError(); 15 16 void navigateToHome(); 17 }
1 package com.nsu.edu.androidmvpdemo.login; 2 3 import android.app.Activity; 4 import android.content.Intent; 5 import android.os.Bundle; 6 import android.view.View; 7 import android.widget.EditText; 8 import android.widget.ProgressBar; 9 import android.widget.Toast; 10 11 import com.nsu.edu.androidmvpdemo.R; 12 13 /** 14 * Created by Anthony on 2016/2/15. 15 * Class Note:MVP模式中View層對應一個activity,這裏是登錄的activity 16 */ 17 public class LoginActivity extends Activity implements LoginView, View.OnClickListener { 18 19 private ProgressBar progressBar; 20 private EditText username; 21 private EditText password; 22 private LoginPresenter presenter; 23 24 @Override 25 protected void onCreate(Bundle savedInstanceState) { 26 super.onCreate(savedInstanceState); 27 setContentView(R.layout.activity_login); 28 29 progressBar = (ProgressBar) findViewById(R.id.progress); 30 username = (EditText) findViewById(R.id.username); 31 password = (EditText) findViewById(R.id.password); 32 findViewById(R.id.button).setOnClickListener(this); 33 34 presenter = new LoginPresenterImpl(this); 35 } 36 37 @Override 38 protected void onDestroy() { 39 presenter.onDestroy(); 40 super.onDestroy(); 41 } 42 43 @Override 44 public void showProgress() { 45 progressBar.setVisibility(View.VISIBLE); 46 } 47 48 @Override 49 public void hideProgress() { 50 progressBar.setVisibility(View.GONE); 51 } 52 53 @Override 54 public void setUsernameError() { 55 username.setError(getString(R.string.username_error)); 56 } 57 58 @Override 59 public void setPasswordError() { 60 password.setError(getString(R.string.password_error)); 61 } 62 63 @Override 64 public void navigateToHome() { 65 // TODO startActivity(new Intent(this, MainActivity.class)); 66 Toast.makeText(this,"login success",Toast.LENGTH_SHORT).show(); 67 // finish(); 68 } 69 70 @Override 71 public void onClick(View v) { 72 presenter.validateCredentials(username.getText().toString(), password.getText().toString()); 73 } 74 75 }
Presenter扮演着view和model的中間層的角色。獲取model層的數據以後構建view層;也能夠收到view層UI上的反饋命令後分發處理邏輯,交給model層作業務操做。它也能夠決定View層的各類操做。數據結構
1 package com.nsu.edu.androidmvpdemo.login; 2 3 /** 4 * Created by Anthony on 2016/2/15. 5 * Class Note:登錄的Presenter 的接口,實現類爲LoginPresenterImpl,完成登錄的驗證,以及銷燬當前view 6 */ 7 public interface LoginPresenter { 8 void validateCredentials(String username, String password); 9 10 void onDestroy(); 11 }
1 package com.nsu.edu.androidmvpdemo.login; 2 3 /** 4 * Created by Anthony on 2016/2/15. 5 * Class Note: 6 * 1 完成presenter的實現。這裏面主要是Model層和View層的交互和操做。 7 * 2 presenter裏面還有個OnLoginFinishedListener, 8 * 其在Presenter層實現,給Model層回調,更改View層的狀態, 9 * 確保 Model層不直接操做View層。若是沒有這一接口在LoginPresenterImpl實現的話, 10 * LoginPresenterImpl只 有View和Model的引用那麼Model怎麼把結果告訴View呢? 11 */ 12 public class LoginPresenterImpl implements LoginPresenter, OnLoginFinishedListener { 13 private LoginView loginView; 14 private LoginModel loginModel; 15 16 public LoginPresenterImpl(LoginView loginView) { 17 this.loginView = loginView; 18 this.loginModel = new LoginModelImpl(); 19 } 20 21 @Override 22 public void validateCredentials(String username, String password) { 23 if (loginView != null) { 24 loginView.showProgress(); 25 } 26 27 loginModel.login(username, password, this); 28 } 29 30 @Override 31 public void onDestroy() { 32 loginView = null; 33 } 34 35 @Override 36 public void onUsernameError() { 37 if (loginView != null) { 38 loginView.setUsernameError(); 39 loginView.hideProgress(); 40 } 41 } 42 43 @Override 44 public void onPasswordError() { 45 if (loginView != null) { 46 loginView.setPasswordError(); 47 loginView.hideProgress(); 48 } 49 } 50 51 @Override 52 public void onSuccess() { 53 if (loginView != null) { 54 loginView.navigateToHome(); 55 } 56 } 57 }
1 package com.nsu.edu.androidmvpdemo.login; 2 3 /** 4 * Created by Anthony on 2016/2/15. 5 * Class Note:登錄事件監聽 6 */ 7 public interface OnLoginFinishedListener { 8 9 void onUsernameError(); 10 11 void onPasswordError(); 12 13 void onSuccess(); 14 }
1 Activity作了一些UI初始化的東西並須要實例化對應LoginPresenter的引用和實現 LoginView的接口,監聽界面動做
2 登錄按鈕按下後即接收到登錄的事件,在onClick裏接收到即經過LoginPresenter的引用把它交給LoginPresenter處理。LoginPresenter接收到了登錄的邏輯就知道要登錄了
3 而後LoginPresenter顯示進度條而且把邏輯交給咱們的Model去處理,也就是這裏面的LoginModel,(LoginModel的實現類LoginModelImpl),同時會把OnLoginFinishedListener也就是LoginPresenter自身傳遞給咱們的Model(LoginModel)。
4 LoginModel處理完邏輯以後,結果經過OnLoginFinishedListener回調通知LoginPresenter
5 LoginPresenter再把結果返回給view層的Activity,最後activity顯示結果
請參考這張類圖:架構
3.1 presenter裏面還有個OnLoginFinishedListener,其在Presenter層實現,給Model層回調,更改View層的狀態,確保 Model層不直接操做View層。
3.2 在一個好的架構中,model層可能只是一個領域層和業務邏輯層的入口,若是咱們參考網上比較火的Uncle Bob clean architecture model層多是一個實現業務用例的交互者,在後續的文章中應該會涉及到這方面的問題,目前能力有限。暫時講解到這裏
本項目github地址:
https://github.com/CameloeAnthony/AndroidMVPDemo
第二例子源碼地址:https://github.com/liuling07/SimpleNews
MVC即Model-View-Controller。M:邏輯模型,V:視圖模型,C:控制器。
MVC模式下,系統框架的類庫被劃分爲3種:模型(Model)、視圖(View)、控制器(Controller)。模型對象負責創建數據結構和相應的行爲操做處理。視圖對象負責在屏幕上渲染出相應的圖形信息展現給用戶看。控制器對象負責截獲用戶的按鍵和屏幕觸摸等事件,協調Model對象和View對象。
用戶與視圖交互,視圖接收並反饋用戶的動做;視圖把用戶的請求傳給相應的控制器,由控制器決定調用哪一個模型,而後由模型調用相應的業務邏輯對用戶請求進行加工處理,若是須要返回數據,模型會把相應的數據返回給控制器,由控制器調用相應的視圖,最終由視圖格式化和渲染返回的數據,對於返回的數據徹底能夠增長用戶體驗效果展示給用戶。
一個模型能夠有多個視圖,一個視圖能夠有多個控制器,一個控制器也能夠有多個模型。
MVC模式結構以下:
圖1-1 MVC模式組件類型的關係和功能
模型(Model):封裝的是數據源和全部基於對這些數據的操做。在一個組件中,Model每每表示組件的狀態和操做狀態的方法。
視圖(View):封裝的是對數據源Model的一種顯示。一個模型能夠由多個視圖,而一個視圖理論上也能夠同不一樣的模型關聯起來。
控制器(Control):封裝的是外界做用於模型的操做。一般,這些操做會轉發到模型上,並調用模型中相應的一個或者多個方法。通常Controller在Model和View之間起到了溝通的做用,處理用戶在View上的輸入,並轉發給Model。這樣Model和View二者之間能夠作到鬆散耦合,甚至能夠彼此不知道對方,而由Controller鏈接起這兩個部分。
MVC應用程序老是由這三個部分組成。Event(事件)致使Controller改變Model或View,或者同時改變二者。只要Controller改變了Model的數據或者屬性,全部依賴的View都會自動更新。相似的,只要Controller改變了View,View會從潛在的Model中獲取數據來刷新本身。MVC模式最先是smalltalk語言研究團提出的,應用於用戶交互應用程序中。
在設計模式中,MVC其實是一個比較高層的模式,它由多個更基本的設計模式組合而成,Model-View的關係其實是Observer模式,模型的狀態和試圖的顯示相互響應,而View-Controller則是由Strategy模式所描述的,View用一個特定的Controller的實例來實現一個特定的響應策略,更換不一樣的Controller,能夠改變View對用戶輸入的響應。而其它的一些設計模式也很容易組合到這個體系中。好比,經過Composite模式,能夠將多個View嵌套組合起來;經過FactoryMethod模式來指定View的Controller,等等。在GOF書的 Introduction中,有一小節是「Design Patterns in Smalltalk MVC」即介紹在MVC模式裏用到的設計模式。它大概向咱們傳達了這樣的信息:合成模式+策略模式+觀察者模式約等於MVC模式(固然MVC模式要多一些 東西)。
使用MVC的好處,一方面,分離數據和其表示,使得添加或者刪除一個用戶視圖變得很容易,甚至能夠在程序執行時動態的進行。Model和View可以單獨的開發,增長了程序了可維護性,可擴展性,並使測試變得更爲容易。另外一方面,將控制邏輯和表現界面分離,容許程序可以在運行時根據工做流、用戶習慣或者模型狀態來動態選擇不一樣的用戶界面。所以,MVC模式普遍用於Web程序、GUI程序的架構。
這裏實現一個Java應用程序。當用戶在圖形化用戶界面輸入一個球體的半徑時,程序將顯示該球體的體積與表面積。咱們首先利用基本MVC模式實現以上程序,而後利用不一樣數量的模型、視圖、控制器結構來擴展該程序。
Model與View的交互使用Observer模式。Model類必須繼承Observable類,View類必須實現接口Observer。正是因爲實現了上述結構,當Model發生改變時(Controller改變Model的狀態),Model就會自動刷新與之相關的View。Controller類主要負責新建Model與View,將view與Mode相關聯,並處理觸發模型值改變的事件。
1 import java.util.Observable; 2 3 //Sphere.java:Model類 4 //必須繼承Observable,在Observable類中,方法addObserver()將視圖與模型相關聯 5 class Sphere extends Observable { 6 7 private double myRadius; 8 9 public void setRadius(double r) { 10 myRadius = r; 11 this.setChanged(); //指示模型已經改變 12 this.notifyObservers(); //通知各個視圖,從父繼承的方法 13 } 14 //...... 15 }
1 import java.util.Observable; 2 import java.util.Observer; 3 import javax.swing.JPanel; 4 5 //TextView.java:View視圖類 6 //當模型Sphere類的狀態發生改變時,與模型相關聯的視圖中的update()方法 7 //就會自動被調用,從而實現視圖的自動刷新 8 public class TextView extends JPanel implements Observer { 9 10 @Override 11 public void update(Observable o, Object arg) { 12 Sphere balloon = (Sphere) o; 13 radiusIn.setText("" + f3.format(balloon.getRadius())); 14 volumeOut.setText("" + f3.format(balloon.volume())); 15 surfAreaOut.setText("" + f3.format(balloon.surfaceArea())); 16 } 17 //...... 18 }
1 import java.awt.Container; 2 import java.awt.event.ActionEvent; 3 import javax.swing.JFrame; 4 import javax.swing.JTextField; 5 6 // SphereWindow.java:Controller類 7 // 它主要新建Model與View,將view與Mode相關聯,並處理事件 8 public class SphereWindow extends JFrame { 9 10 public SphereWindow() { 11 super("Spheres: volume and surface area"); 12 model = new Sphere(0, 0, 100); //新建Model 13 TextView view = new TextView(); //新建View 14 model.addObserver(view); //將View與Model相關聯 15 view.update(model, null); //初始化視圖,之後就會根據Model的變化自動刷新 16 view.addActionListener(this); 17 Container c = getContentPane(); 18 c.add(view); 19 } 20 21 //處理事件:改變Model的狀態 22 public void actionPerformed(ActionEvent e) { 23 JTextField t = (JTextField) e.getSource(); 24 double r = Double.parseDouble(t.getText()); 25 model.setRadius(r); 26 } 27 //...... 28 }
這種MVC模式的程序具備極其良好的可擴展性。它能夠輕鬆實現一個模型的多個視圖;能夠採用多個控制器;能夠實現當模型改變時,全部視圖自動刷新;可使全部的控制器相互獨立工做。
好比實現一個模型、兩個視圖和一個控制器的程序。當用戶在圖形化用戶界面輸入一個球體的半徑,程序除顯示該球體的體積與表面積外,還將圖形化顯示該球體。該程序的4個類之間的示意圖以下: