10.1Android中MVP編程模式

Android中MVP編程模式android

MVP模式的核心思想: MVP把Activity中的UI邏輯抽象成View接口,把業務邏輯抽象成接口,Model類仍是原來的Model。git

MVC 其中View層其實就是程序的UI界面,用於向用戶展現數據以及接收用戶的輸入 而Model層就是JavaBean實體類,用於保存實例數據 Controller控制器用於更新UI界面和數據實例 View層接受用戶的輸入,而後經過Controller修改對應的Model實例;同時,當Model實例的數據發生變化的時候,須要修改UI界面,能夠經過Controller更新界面。 View層也能夠直接更新Model實例的數據,而不用每次都經過Controller,這樣對於一些簡單的數據更新工做會變得方便許多。github

MVP數據庫

MVP與MVC最不一樣的一點是 M與V是不直接關聯的也是就Model與View不存在直接關係,這二者之間間隔着的是Presenter層 Model編程

Model 是用戶界面須要顯示數據的抽象,也能夠理解爲從業務數據(結果)那裏到用戶界面的抽象(Business rule, data access, model classes)網絡

View 視圖這一層體現的很輕薄,負責顯示數據、提供友好界面跟用戶交互就行。MVP下Activity和Fragment體如今了這一層,Activity通常也就作加載UI視圖、設置監聽再交由Presenter處理的一些工做,因此也就須要持有相應Presenter的引用。例如,Activity上滾動列表時隱藏或者顯示Acionbar(Toolbar),這樣的UI邏輯時也應該在這一層。另外在View上輸入的數據作一些判斷時,例如,EditText的輸入數據,假如是簡單的非空判斷則能夠做爲View層的邏輯,而當須要對EditText的數據進行更復雜的比較時,如從數據庫獲取本地數據進行判斷時明顯須要通過Model層才能返回了,因此這些細節須要本身掂量。app

Presenter Presenter這一層處理着程序各類邏輯的分發,收到View層UI上的反饋命令、定時命令、系統命令等指令後分發處理邏輯交由業務層作具體的業務操做,而後將獲得的 Model 給 View 顯示。 這就是MVP模式,如今這樣的話,Activity的工做的簡單了,只用來響應生命週期,其餘工做都丟到Presenter中去完成。從上圖能夠看出, Presenter是Model和View之間的橋樑 ,爲了讓結構變得更加簡單, View並不能直接對Model進行操做,這也是MVP與MVC最大的不一樣之處 。異步

優勢 分離了視圖邏輯和業務邏輯,下降了耦合 Activity只處理生命週期的任務,代碼變得更加簡潔 視圖邏輯和業務邏輯分別抽象到了 View和Presenter的接口 中去,提升代碼的可閱讀性 Presenter被抽象成接口,能夠有多種具體的實現,因此方便進行單元測試 把業務邏輯抽到Presenter中去,避免後臺線程引用着Activity致使Activity的資源沒法被系統回收從而引發內存泄露和OOM 代碼變得更加簡潔ide

使用MVP以後,Activity就能瘦身許多了,基本上只有FindView、SetListener以及Init的代碼。其餘的就是對Presenter的調用,還有對View接口的實現。這種情形下閱讀代碼就容易多了,並且你只要看Presenter的接口,就能明白這個模塊都有哪些業務,很快就能定位到具體代碼。Activity變得容易看懂,容易維護,之後要調整業務、刪減功能也就變得簡單許多。post

方便進行單元測試 MVP中,因爲業務邏輯都在Presenter裏,咱們徹底能夠寫一個PresenterTest的實現類繼承Presenter的接口,如今只要在Activity裏把Presenter的建立換成PresenterTest,就能進行單元測試了,測試完再換回來便可。萬一發現還得進行測試,那就再換成PresenterTest吧。

避免內存泄露 Android APP 發生OOM的最大緣由就是出現內存泄露形成APP的內存不夠用,而形成內存泄露的兩大緣由之一就是Activity泄露(Activity Leak)(另外一個緣由是Bitmap泄露(Bitmap Leak))

Java一個強大的功能就是其虛擬機的內存回收機制,這個功能使得Java用戶在設計代碼的時候,不用像C++用戶那樣考慮對象的回收問題。然而,Java用戶老是喜歡隨便寫一大堆對象,而後幻想着虛擬機能幫他們處理好內存的回收工做。但是虛擬機在回收內存的時候,只會回收那些沒有被引用的對象,被引用着的對象由於還可能會被調用,因此不能回收。

Activity是有生命週期的,用戶隨時可能切換Activity,當APP的內存不夠用的時候,系統會回收處於後臺的Activity的資源以免OOM。 採用傳統的MV模式,一大堆異步任務和對UI的操做都放在Activity裏面,好比你可能從網絡下載一張圖片,在下載成功的回調裏把圖片加載到 Activity 的 ImageView 裏面,因此異步任務保留着對Activity的引用。這樣一來,即便Activity已經被銷燬(onDestroy已經執行),這些異步任務仍然保留着對Activity實例的引用,因此係統就沒法回收這個Activity實例了,結果就是Activity Leak。

Android的組件中,Activity對象每每是在堆(Java Heap)裏佔最多內存的,因此係統會優先回收Activity對象,若是有Activity Leak,APP很容易由於內存不夠而OOM。

採用MVP模式,只要在當前的Activity的onDestroy裏,分離異步任務對Activity的引用,就能避免 Activity Leak。

MVP 使用 MVP的主要特色就是把Activity裏的許多邏輯都抽離到View和Presenter接口中去,並由具體的實現類來完成。

建立IPresenter接口,把全部業務邏輯的接口都放在這裏,並建立它的實現PresenterCompl(在這裏能夠方便地查看業務功能,因爲接口能夠有多種實現因此也方便寫單元測試),IPresenter持有 IView,調用 IView 中的方法

建立IView接口,把全部視圖邏輯的接口都放在這裏,其實現類是當前的Activity/Fragment 由UML圖能夠看出, Activity裏包含了一個IPresenter,而PresenterCompl裏又包含了一個IView而且依賴了Model 。Activity裏只保留對IPresenter的調用,其它工做所有留到PresenterCompl中實現 Model並非必須有的,可是必定會有View和Presenter

DMEO 簡單的登錄界面的例子 登錄 view 接口 package io.github.xuyushi.androidmvpdemo.Login.view;

/**

  • Created by xuyushi on 16/2/28. */ public interface ILoginView { void clearEditText();

    void showProgress();

    void hideProgress();

    void setUsernameError();

    void setPasswordError();

    String getUsername();

    String getPassword();

    void loginSuccess();

}

登錄Presenter接口 package io.github.xuyushi.androidmvpdemo.Login.presenter;

/**

  • Created by xuyushi on 16/2/28. */ public interface ILoginPresenter { void doLogin(String username, String password);

    void clear();

    void onDestroy(); }

實現Presenter接口 package io.github.xuyushi.androidmvpdemo.Login.presenter;

import android.os.Handler;

import io.github.xuyushi.androidmvpdemo.Login.model.User; import io.github.xuyushi.androidmvpdemo.Login.view.ILoginView;

/**

  • Created by xuyushi on 16/2/28. */ public class LoginPresenter implements ILoginPresenter { private ILoginView mLoginView; private User mUser;

    public LoginPresenter(ILoginView loginView) { this.mLoginView = loginView; initUser(); }

    private void initUser() { mUser = new User(mLoginView.getUsername(), mLoginView.getPassword()); }

    @Override public void doLogin(String username, String password) { mLoginView.showProgress(); new Handler().postDelayed(new Runnable() { @Override public void run() { mLoginView.hideProgress(); int code = mUser.checkUserValidity(mLoginView.getUsername(), mLoginView.getPassword()); if (code == -1) { mLoginView.setPasswordError(); } else if (code == 0) { mLoginView.loginSuccess(); } } }, 2000); }

    @Override public void clear() { mLoginView.clearEditText(); }

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

定義model package io.github.xuyushi.androidmvpdemo.Login.model;

/**

  • Created by xuyushi on 16/2/28. */ 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; }

    public User(String username, String password) { this.username = username; this.password = password; }

    public int checkUserValidity(String username, String password) { if (username == null || password == null || username.isEmpty() || password.isEmpty()) { return -1; } return 0; } }

在 Activity 中實現 view接口 package io.github.xuyushi.androidmvpdemo.Login.view;

import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.view.View; import android.widget.Button; import android.widget.EditText; import android.widget.ProgressBar; import android.widget.Toast;

import butterknife.Bind; import butterknife.ButterKnife; import io.github.xuyushi.androidmvpdemo.Login.presenter.ILoginPresenter; import io.github.xuyushi.androidmvpdemo.Login.presenter.LoginPresenter; import io.github.xuyushi.androidmvpdemo.R;

public class LoginActivity extends AppCompatActivity implements ILoginView, View.OnClickListener {

private ILoginPresenter mLoginPresenter;

@Bind(R.id.et_username)
EditText etUsername;
@Bind(R.id.et_passwrod)
EditText etPasswrod;
@Bind(R.id.bt_enter)
Button btEnter;
@Bind(R.id.bt_clear)
Button btClear;
@Bind(R.id.progress)
ProgressBar progress;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    ButterKnife.bind(this);
    mLoginPresenter = new LoginPresenter(this);
    btEnter.setOnClickListener(this);
    btClear.setOnClickListener(this);
}

@Override
public void clearEditText() {
    etPasswrod.setText("");
    etUsername.setText("");
}

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

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

@Override
public void setUsernameError() {
    etUsername.setError("username error");
}

@Override
public void setPasswordError() {
    etPasswrod.setError("password error");

}

@Override
public String getUsername() {
    return etUsername.getText().toString();
}

@Override
public String getPassword() {
    return etPasswrod.getText().toString();
}

@Override
public void loginSuccess() {
    //start act Main
    Toast.makeText(this, "login success", Toast.LENGTH_SHORT);
    finish();
}

@Override
public void onClick(View v) {
    switch (v.getId()){
        case R.id.bt_clear:
            mLoginPresenter.clear();
            break;
        case R.id.bt_enter:
            mLoginPresenter.doLogin(etUsername.getText().toString(),
                    etPasswrod.getText().toString());
            break;
    }
}

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

} 源碼地址 https://github.com/xuyushi/AndroidMVPDemo

Presenter至關於MVC中的控制器,負責Model和View的溝通,做爲Model和View通信的橋樑,Presenter類中必須持有它們的引用。

相關文章
相關標籤/搜索