Android進階-Android MVP模式詳解


一、MVP概述

MVP,全稱 Model-View-Presenter,即模型-視圖-層現器。

提到MVP,就必須要先介紹一下它的前輩MVC,因為MVP正是基於MVC的基礎發展而來的。兩個之間的關系也是源遠流長。

MVC,全稱Model-View-Controller,即模型-視圖-控制器。

View:對應於布局文件

Model:業務邏輯和實體模型

Controllor:對應於Activity

但是View對應於布局文件,其實能做的事情特別少,實際上關於該布局文件中的數據綁定的操作,事件處理的代碼都在Activity中,造成了Activity既像View又像Controller,使得Activity變得臃腫。

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

View 對應於Activity,負責View的繪制以及與用戶交互

Model 依然是業務邏輯和實體模型

Presenter 負責完成View於Model間的交互

下面兩幅圖通過數據與視圖之間的交互清楚地展示了這種變化:

 

MVC模式下實際上就是Activty與Model之間交互,View完全獨立出來了。

 

MVP模式通過Presenter實現數據和視圖之間的交互,簡化了Activity的職責。同時即避免了View和Model的直接聯系,又通過Presenter實現兩者之間的溝通。

總結:MVP模式減少了Activity的職責,簡化了Activity中的代碼,將復雜的邏輯代碼提取到了Presenter中進行處理,模塊職責划分明顯,層次清晰。與之對應的好處就是,耦合度更低,更方便的進行測試。

 

 MVC和MVP的區別

MVC中是允許Model和View進行交互的,而MVP中很明顯,Model與View之間的交互由Presenter完成。還有一點就是Presenter與View之間的交互是通過接口的。

還有一點注意:MVC中V對應的是布局文件,MVP中V對應的是Activity。

 

二、MVP的簡單使用

大多數MVP模式的示例都使用登錄案例進行介紹。因為簡單方便,同時能提現出MVP的特點。今天我們也以此例進行學習。 使用MVP的好處之一就是模塊職責划分明顯,層次清晰。 該例的結構圖即可展現此優點。

1.Model層

在本例中,M0del層負責對從登錄頁面獲取地帳號密碼進行驗證(一般需要請求服務器進行驗證,本例直接模擬這一過程)。 從上圖的包結構圖中可以看出,Model層包含內容:

①實體類bean

②接口,表示Model層所要執行的業務邏輯

③接口實現類,具體實現業務邏輯,包含的一些主要方法

下面以代碼的形式一一展開。

①實體類bean

public class User {
    private String password;
    private String username;

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    @Override
    public String toString() {
        return "User{" +
                "password='" + password + '\'' +
                ", username='" + username + '\'' +
                '}';
    }
}

封裝了用戶名、密碼,方便數據傳遞。

②接口

public interface LoginModel {
    void login(User user, OnLoginFinishedListener listener);
}

其中OnLoginFinishedListener 是presenter層的接口,方便實現回調presenter,通知presenter業務邏輯的返回結果,具體在presenter層介紹。

③接口實現類

 

public class LoginModelImpl implements LoginModel {
    @Override
    public void login(User user, final OnLoginFinishedListener listener) {
        final String username = user.getUsername();
        final String password = user.getPassword();
        new Handler().postDelayed(new Runnable() {
            @Override public void run() {
                boolean error = false;
                if (TextUtils.isEmpty(username)){
                    listener.onUsernameError();//model層里面回調listener
                    error = true;
                }
                if (TextUtils.isEmpty(password)){
                    listener.onPasswordError();
                    error = true;
                }
                if (!error){
                    listener.onSuccess();
                }
            }
        }, 2000);
    }
}

實現Model層邏輯:延時模擬登陸(2s),如果用戶名或者密碼為空則登陸失敗,否則登陸成功。

 

2.View層

視圖:將Modle層請求的數據呈現給用戶。一般的視圖都只是包含用戶界面(UI),而不包含界面邏輯,界面邏輯由Presenter來實現。

從上圖的包結構圖中可以看出,View包含內容:

①接口,上面我們說過Presenter與View交互是通過接口。其中接口中方法的定義是根據Activity用戶交互需要展示的控件確定的。

②接口實現類,將上述定義的接口中的方法在Activity中對應實現具體操作。

下面以代碼的形式一一展開。

 ①接口

 

public interface LoginView {
    //login是個耗時操作,我們需要給用戶一個友好的提示,一般就是操作ProgressBar
    void showProgress();

    void hideProgress();
   //login當然存在登錄成功與失敗的處理,失敗給出提示
    void setUsernameError();

    void setPasswordError();
   //login成功,也給個提示
    void showSuccess();
}

上述5個方法都是presenter根據model層返回結果需要view執行的對應的操作。

②接口實現類

即對應的登錄的Activity,需要實現LoginView接口。

 

public class LoginActivity extends AppCompatActivity implements LoginView, View.OnClickListener {
    private ProgressBar progressBar;
    private EditText username;
    private EditText password;
    private LoginPresenter presenter;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_login);
        progressBar = (ProgressBar) findViewById(R.id.progress);
        username = (EditText) findViewById(R.id.username);
        password = (EditText) findViewById(R.id.password);
        findViewById(R.id.button).setOnClickListener(this);
       //創建一個presenter對象,當點擊登錄按鈕時,讓presenter去調用model層的login()方法,驗證帳號密碼
        presenter = new LoginPresenterImpl(this);
    }

    @Override
    protected void onDestroy() {
        presenter.onDestroy();
        super.onDestroy();
    }

    @Override
    public void showProgress() {
        progressBar.setVisibility(View.VISIBLE);
    }

    @Override
    public void hideProgress() {
        progressBar.setVisibility(View.GONE);
    }

    @Override
    public void setUsernameError() {
        username.setError(getString(R.string.username_error));
    }

    @Override
    public void setPasswordError() {
        password.setError(getString(R.string.password_error));
    }

    @Override
    public void showSuccess() {
         progressBar.setVisibility(View.GONE);
        Toast.makeText(this,"login success",Toast.LENGTH_SHORT).show();
    }

    @Override
    public void onClick(View v) {
        User user = new User();
        user.setPassword(password.getText().toString());
        user.setUsername(username.getText().toString());
        presenter.validateCredentials(user);
    }

}

View層實現Presenter層需要調用的控件操作,方便Presenter層根據Model層返回的結果進行操作View層進行對應的顯示。

 

3.Presenter層

Presenter是用作Model和View之間交互的橋梁。 從上圖的包結構圖中可以看出,Presenter包含內容:

①接口,包含Presenter需要進行Model和View之間交互邏輯的接口,以及上面提到的Model層數據請求完成后回調的接口。

②接口實現類,即實現具體的Presenter類邏輯。

下面以代碼的形式一一展開。

①接口

 

public interface OnLoginFinishedListener {
    void onUsernameError();

    void onPasswordError();

    void onSuccess();
}

當Model層得到請求的結果,需要回調Presenter層,讓Presenter層調用View層的接口方法。

public interface LoginPresenter {
    void validateCredentials(User user);

    void onDestroy();
}

登陸的Presenter 的接口,實現類為LoginPresenterImpl,完成登陸的驗證,以及銷毀當前view。

②接口實現類

 

public class LoginPresenterImpl implements LoginPresenter, OnLoginFinishedListener {
    private LoginView loginView;
    private LoginModel loginModel;

    public LoginPresenterImpl(LoginView loginView) {
        this.loginView = loginView;
        this.loginModel = new LoginModelImpl();
    }

    @Override
    public void validateCredentials(User user) {
        if (loginView != null) {
            loginView.showProgress();
        }

        loginModel.login(user, this);
    }

    @Override
    public void onDestroy() {
        loginView = null;
    }

    @Override
    public void onUsernameError() {
        if (loginView != null) {
            loginView.setUsernameError();
            loginView.hideProgress();
        }
    }

    @Override
    public void onPasswordError() {
        if (loginView != null) {
            loginView.setPasswordError();
            loginView.hideProgress();
        }
    }

    @Override
    public void onSuccess() {
        if (loginView != null) {
            loginView.showSuccess();
        }
    }
}

由於presenter完成二者的交互,那么肯定需要二者的實現類(通過傳入參數,或者new)。

presenter里面有個OnLoginFinishedListener, 其在Presenter層實現,給Model層回調,更改View層的狀態, 確保 Model層不直接操作View層。

示例展示:

代碼地址

 

三、總結

MVP模式的整個核心流程:

View與Model並不直接交互,而是使用Presenter作為View與Model之間的橋梁。其中Presenter中同時持有View層的Interface的引用以及Model層的引用,而View層持有Presenter層引用。當View層某個界面需要展示某些數據的時候,首先會調用Presenter層的引用,然后Presenter層會調用Model層請求數據,當Model層數據加載成功之后會調用Presenter層的回調方法通知Presenter層數據加載情況,最后Presenter層再調用View層的接口將加載后的數據展示給用戶。

 

 

 

轉載自: https://lrh1993.gitbooks.io/android_interview_guide/content/android/advance/mvp.html

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM