淺談 MVP in Android

來源:互聯網
上載者:User

標籤:mvp   android   

轉載請標明出處:
http://blog.csdn.net/lmj623565791/article/details/46596109;
本文出自:【張鴻洋的部落格】

一、概述

對於MVP(Model View Presenter),大多數人都能說出一二:“MVC的演化版本”,“讓Model和View完全解耦”等等。本篇博文僅是為了做下記錄,提出一些自己的看法,和協助大家如何針對一個Activity頁面去編寫針對MVP風格的代碼。

對於MVP,我的內心有一個問題:

為何這個模式出來後,就能被廣大的Android的程式員接受呢?

問了些程式員,他們對於MVP的普遍的認識是:“代碼很清晰,不過增加了很多類”。我在第一次看到MVP的時候,看了一個demo,看完以後覺得非常nice(但是回過頭來,自己想個例子寫,就頭疼寫不出來,當然這在後文會說)。nice的原因還是因為,這個模式的確讓代碼的清晰度有了很大的提升。

那麼,提升一般都是對比出來的,回顧下,沒有應用MVP的代碼結構。很多人說明顯是MVC麼:

  • View:對應於布局檔案
  • Model:商務邏輯和實體模型
  • Controllor:對應於Activity

看起來的確像那麼回事,但是細細的想想這個View對應於布局檔案,其實能做的事情特別少,實際上關於該布局檔案中的資料繫結的操作,事件處理的代碼都在Activity中,造成了Activity既像View又像Controller(當然了Data-Binder的出現,可能會讓View更像View吧)。這可能也就是為何,在該文中有一句這樣的話:

Most of the modern Android applications just use View-Model architecture,everything is connected with Activity.

而當將架構改為MVP以後,Presenter的出現,將Actvity視為View層,Presenter負責完成View層與Model層的互動。現在是這樣的:

  • View 對應於Activity,負責View的繪製以及與使用者互動
  • Model 依然是商務邏輯和實體模型
  • Presenter 負責完成View於Model間的互動

ok,先簡單瞭解下,文中會有例子到時候可以直觀的感受下。

小總結下,也就是說,之所以讓人覺得耳目一新,是因為這次的跳躍是從並不標準的MVCMVP的一個轉變,減少了Activity的職責,簡化了Activity中的代碼,將複雜的邏輯代碼提取到了Presenter中進行處理。與之對應的好處就是,耦合度更低,更方便的進行測試。借用兩張圖(出自:該文),代表上述的轉變:

轉變為:

二、MVP 與 MVC 區別

ok,上面說了一堆理論,下面我們還是需要看一看MVC與MVP的一個區別,請看(來自:本文):

其實最明顯的區別就是,MVC中是允許Model和View進行互動的,而MVP中很明顯,Model與View之間的互動由Presenter完成。還有一點就是Presenter與View之間的互動是通過介面的(代碼中會體現)。

還有一堆概念性的東西,以及優點就略了,有興趣自行百度。下面還是通過一些簡單的需求來展示如何編寫MVP的demo。

三、Simple Login Demo

看到這樣的效果,先看下完工後的項目結構:

ok,接下來開始一步一步的編寫思路。

(一)Model

首先實體類User不用考慮這個肯定有,其次從可以看到至少有一個業務方法login(),這兩點沒什麼難度,我們首先完成:

package com.zhy.blogcodes.mvp.bean;/** * Created by zhy on 15/6/18. */public class User{    private String username ;    private String password ;    public String getUsername()    {        return username;    }    public void setUsername(String username)    {        this.username = username;    }    public String getPassword()    {        return password;    }    public void setPassword(String password)    {        this.password = password;    }}
package com.zhy.blogcodes.mvp.biz;/** * Created by zhy on 15/6/19. */public interface IUserBiz{    public void login(String username, String password, OnLoginListener loginListener);}
package com.zhy.blogcodes.mvp.biz;import com.zhy.blogcodes.mvp.bean.User;/** * Created by zhy on 15/6/19. */public class UserBiz implements IUserBiz{    @Override    public void login(final String username, final String password, final OnLoginListener loginListener)    {        //類比子線程耗時操作        new Thread()        {            @Override            public void run()            {                try                {                    Thread.sleep(2000);                } catch (InterruptedException e)                {                    e.printStackTrace();                }                //類比登入成功                if ("zhy".equals(username) && "123".equals(password))                {                    User user = new User();                    user.setUsername(username);                    user.setPassword(password);                    loginListener.loginSuccess(user);                } else                {                    loginListener.loginFailed();                }            }        }.start();    }}
package com.zhy.blogcodes.mvp.biz;import com.zhy.blogcodes.mvp.bean.User;/** * Created by zhy on 15/6/19. */public interface OnLoginListener{    void loginSuccess(User user);    void loginFailed();}

實體類不用說,至於業務類,我們抽取了一個介面,一個實作類別這也很常見~~login方法,一般肯定是串連伺服器的,是個耗時操作,所以我們開闢了子線程,Thread.sleep(2000)類比了耗時,由於是耗時操作,所以我們通過一個回調介面來通知登入的狀態。

其實這裡還是比較好寫的,因為和傳統寫法沒區別。

(二) View

上面我們說過,Presenter與View互動是通過介面。所以我們這裡需要定義一個ILoginView,痛點就在於應該有哪些方法,我們看一眼:

可以看到我們有兩個按鈕,一個是login,一個是clear;

login說明了要有使用者名稱、密碼,那麼對應兩個方法:

    String getUserName();    String getPassword();

再者login是個耗時操作,我們需要給使用者一個友好的提示,一般就是操作ProgressBar,所以再兩個:

    void showLoading();    void hideLoading();

login當然存在登入成功與失敗的處理,我們主要看成功我們是跳轉Activity,而失敗可能是去給個提醒:

    void toMainActivity(User user);    void showFailedError();

ok,login這個方法我們分析完了~~還剩個clear那就簡單了:

    void clearUserName();    void clearPassword();

綜上,介面完整為:

package com.zhy.blogcodes.mvp.view;import com.zhy.blogcodes.mvp.bean.User;/** * Created by zhy on 15/6/19. */public interface IUserLoginView{    String getUserName();    String getPassword();    void clearUserName();    void clearPassword();    void showLoading();    void hideLoading();    void toMainActivity(User user);    void showFailedError();}

有了介面,實現就太好寫了~~~

總結下,對於View的介面,去觀察功能上的操作,然後考慮:

  • 該操作需要什嗎?(getUserName, getPassword)
  • 該操作的結果,對應的反饋?(toMainActivity, showFailedError)
  • 該操作過程中對應的友好的互動?(showLoading, hideLoading)

下面貼一下我們的View的實作類別,哈,其實就是Activity,文章開始就說過,MVP中的View其實就是Activity。

package com.zhy.blogcodes.mvp;import android.os.Bundle;import android.support.v7.app.ActionBarActivity;import android.view.View;import android.widget.Button;import android.widget.EditText;import android.widget.ProgressBar;import android.widget.Toast;import com.zhy.blogcodes.R;import com.zhy.blogcodes.mvp.bean.User;import com.zhy.blogcodes.mvp.presenter.UserLoginPresenter;import com.zhy.blogcodes.mvp.view.IUserLoginView;public class UserLoginActivity extends ActionBarActivity implements IUserLoginView{    private EditText mEtUsername, mEtPassword;    private Button mBtnLogin, mBtnClear;    private ProgressBar mPbLoading;    private UserLoginPresenter mUserLoginPresenter = new UserLoginPresenter(this);    @Override    protected void onCreate(Bundle savedInstanceState)    {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_user_login);        initViews();    }    private void initViews()    {        mEtUsername = (EditText) findViewById(R.id.id_et_username);        mEtPassword = (EditText) findViewById(R.id.id_et_password);        mBtnClear = (Button) findViewById(R.id.id_btn_clear);        mBtnLogin = (Button) findViewById(R.id.id_btn_login);        mPbLoading = (ProgressBar) findViewById(R.id.id_pb_loading);        mBtnLogin.setOnClickListener(new View.OnClickListener()        {            @Override            public void onClick(View v)            {                mUserLoginPresenter.login();            }        });        mBtnClear.setOnClickListener(new View.OnClickListener()        {            @Override            public void onClick(View v)            {                mUserLoginPresenter.clear();            }        });    }    @Override    public String getUserName()    {        return mEtUsername.getText().toString();    }    @Override    public String getPassword()    {        return mEtPassword.getText().toString();    }    @Override    public void clearUserName()    {        mEtUsername.setText("");    }    @Override    public void clearPassword()    {        mEtPassword.setText("");    }    @Override    public void showLoading()    {        mPbLoading.setVisibility(View.VISIBLE);    }    @Override    public void hideLoading()    {        mPbLoading.setVisibility(View.GONE);    }    @Override    public void toMainActivity(User user)    {        Toast.makeText(this, user.getUsername() +                " login success , to MainActivity", Toast.LENGTH_SHORT).show();    }    @Override    public void showFailedError()    {        Toast.makeText(this,                "login failed", Toast.LENGTH_SHORT).show();    }}

對於在Activity中實現我們上述定義的介面,是一件很容易的事,畢竟介面引導我們去完成。

最後看我們的Presenter。

(三)Presenter

Presenter是用作Model和View之間互動的橋樑,那麼應該有什麼方法呢?

其實也是主要看該功能有什麼操作,比如本例,兩個操作:login和clear。

package com.zhy.blogcodes.mvp.presenter;import android.os.Handler;import com.zhy.blogcodes.mvp.bean.User;import com.zhy.blogcodes.mvp.biz.IUserBiz;import com.zhy.blogcodes.mvp.biz.OnLoginListener;import com.zhy.blogcodes.mvp.biz.UserBiz;import com.zhy.blogcodes.mvp.view.IUserLoginView;/** * Created by zhy on 15/6/19. */public class UserLoginPresenter{    private IUserBiz userBiz;    private IUserLoginView userLoginView;    private Handler mHandler = new Handler();    public UserLoginPresenter(IUserLoginView userLoginView)    {        this.userLoginView = userLoginView;        this.userBiz = new UserBiz();    }    public void login()    {        userLoginView.showLoading();        userBiz.login(userLoginView.getUserName(), userLoginView.getPassword(), new OnLoginListener()        {            @Override            public void loginSuccess(final User user)            {                //需要在UI線程執行                mHandler.post(new Runnable()                {                    @Override                    public void run()                    {                        userLoginView.toMainActivity(user);                        userLoginView.hideLoading();                    }                });            }            @Override            public void loginFailed()            {                //需要在UI線程執行                mHandler.post(new Runnable()                {                    @Override                    public void run()                    {                        userLoginView.showFailedError();                        userLoginView.hideLoading();                    }                });            }        });    }    public void clear()    {        userLoginView.clearUserName();        userLoginView.clearPassword();    }}

注意上述代碼,我們的presenter完成二者的互動,那麼肯定需要二者的實作類別。大致就是從View中擷取需要的參數,交給Model去執行業務方法,執行的過程中需要的反饋,以及結果,再讓View進行做對應的顯示。

ok,拿到一個例子經過上述的分解基本就能完成。以上純屬個人見解,歡迎討論和吐槽~ have a nice day ~。

源碼點擊下載

公眾號:hongyangAndroid
(歡迎關注,第一時間推送博文資訊)

參考資料
  • https://github.com/zhengxiaopeng/Rocko-Android-Demos/tree/master/android-mvp
  • https://github.com/antoniolg/androidmvp
  • https://github.com/pedrovgs/EffectiveAndroidUI
  • http://zhengxiaopeng.com/2015/02/06/Android%E4%B8%AD%E7%9A%84MVP/
  • http://magenic.com/Blog/Post/6/An-MVP-Pattern-for-Android
  • http://antonioleiva.com/mvp-android/
  • http://konmik.github.io/introduction-to-model-view-presenter-on-android.html

淺談 MVP in Android

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.