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類中必須持有它們的引用。