1、什麼是MVP?android
MVP (Model View Presenter)模式由MVC模式演變而來,它將View層和邏輯層分離。git
2、爲何使用MVP?github
將View層和邏輯層分離後有利於拓展,好比當前代碼屬於來自本地數據庫,若是需求變動數據來自網絡,咱們就沒必要重寫整個View層。經過MVP咱們將大部分邏輯抽出Activity,寫在Presenter層。分層後就能夠各司其職,也方便測試。數據庫
3、各層的職責。編程
4、實例.網絡
一個登錄的例子(項目代碼在這裏下載:https://github.com/gatsbydhn/androidmvp):ide
View層:post
View接口,Activity實現了該接口,方便之後拓展:測試
1 public interface LoginView { 2 void showProgress(); 3 4 void hideProgress(); 5 6 void setUsernameError(); 7 8 void setPasswordError(); 9 10 void navigateToHome(); 11 }
Activity:this
1 public class LoginActivity extends Activity implements LoginView, View.OnClickListener { 2 3 private ProgressBar progressBar; 4 private EditText username; 5 private EditText password; 6 private LoginPresenter presenter; 7 8 @Override 9 protected void onCreate(Bundle savedInstanceState) { 10 super.onCreate(savedInstanceState); 11 setContentView(R.layout.activity_login); 12 13 progressBar = (ProgressBar) findViewById(R.id.progress); 14 username = (EditText) findViewById(R.id.username); 15 password = (EditText) findViewById(R.id.password); 16 findViewById(R.id.button).setOnClickListener(this); 17 18 presenter = new LoginPresenterImpl(this); 19 } 20 21 @Override protected void onDestroy() { 22 presenter.onDestroy(); 23 super.onDestroy(); 24 } 25 26 @Override public void showProgress() { 27 progressBar.setVisibility(View.VISIBLE); 28 } 29 30 @Override public void hideProgress() { 31 progressBar.setVisibility(View.GONE); 32 } 33 34 @Override public void setUsernameError() { 35 username.setError(getString(R.string.username_error)); 36 } 37 38 @Override public void setPasswordError() { 39 password.setError(getString(R.string.password_error)); 40 } 41 42 @Override public void navigateToHome() { 43 startActivity(new Intent(this, MainActivity.class)); 44 finish(); 45 } 46 47 @Override public void onClick(View v) { 48 presenter.validateCredentials(username.getText().toString(), password.getText().toString()); 49 } 50 }
分析:如前面所說Activilty裏面的方法分爲兩類:1.修改控件屬性如showProgress(),hideProgress()等,這些接口將被Presenter調用。2.響應外部事件好比onClick(),onDestroy()等,調用Presenter來完成具體的任務。
下面是Presenter層:
接口,方便拓展:
1 public interface LoginPresenter { 2 void validateCredentials(String username, String password); 3 4 void onDestroy(); 5 }
實現類:
1 public class LoginPresenterImpl implements LoginPresenter, LoginInteractor.OnLoginFinishedListener { 2 3 private LoginView loginView; 4 private LoginInteractor loginInteractor; 5 6 public LoginPresenterImpl(LoginView loginView) { 7 this.loginView = loginView; 8 this.loginInteractor = new LoginInteractorImpl(); 9 } 10 11 @Override public void validateCredentials(String username, String password) { 12 if (loginView != null) { 13 loginView.showProgress(); 14 } 15 16 loginInteractor.login(username, password, this); 17 } 18 19 @Override public void onDestroy() { 20 loginView = null; 21 } 22 23 @Override public void onUsernameError() { 24 if (loginView != null) { 25 loginView.setUsernameError(); 26 loginView.hideProgress(); 27 } 28 } 29 30 @Override public void onPasswordError() { 31 if (loginView != null) { 32 loginView.setPasswordError(); 33 loginView.hideProgress(); 34 } 35 } 36 37 @Override public void onSuccess() { 38 if (loginView != null) { 39 loginView.navigateToHome(); 40 } 41 } 42 }
分析:這裏使用面向接口編程,方便之後拓展。Presenter引用Model層的LoginInteractor,完成View交給的任務validateCredentials(),該方法中調用LoginInteractor獲取登錄數據。
Model層:
接口:
1 public interface LoginInteractor { 2 3 interface OnLoginFinishedListener { 4 void onUsernameError(); 5 6 void onPasswordError(); 7 8 void onSuccess(); 9 } 10 11 void login(String username, String password, OnLoginFinishedListener listener); 12 13 }
實現類:
1 public class LoginInteractorImpl implements LoginInteractor { 2 3 @Override 4 public void login(final String username, final String password, final OnLoginFinishedListener listener) { 5 // Mock login. I'm creating a handler to delay the answer a couple of seconds 6 new Handler().postDelayed(new Runnable() { 7 @Override public void run() { 8 boolean error = false; 9 if (TextUtils.isEmpty(username)){ 10 listener.onUsernameError(); 11 error = true; 12 } 13 if (TextUtils.isEmpty(password)){ 14 listener.onPasswordError(); 15 error = true; 16 } 17 if (!error){ 18 listener.onSuccess(); 19 } 20 } 21 }, 2000); 22 } 23 }
分析:登錄應該要從數據庫獲取數據驗證帳號密碼是否正確,這裏省略了這一步驟,可是要明白,這一層的職責是提供數據,無論是從數據庫仍是網絡。