Android进阶-MVP
一、什么是MVP?
MVP (Model View Presenter)模式由MVC模式演变而来,它将View层和逻辑层分离。
二、为什么使用MVP?
将View层和逻辑层分离后有利于拓展,比如当前代码属于来自本地数据库,如果需求变更数据来自网络,我们就不必重写整个View层。通过MVP我们将大部分逻辑抽出Activity,写在Presenter层。分层后就可以各司其职,也方便测试。
三、各层的职责。
- Presenter的职责:从Modle层获取数据经过处理后交给View层展示。
- View的职责:MVP将Activity划在View层,Activity引用Presenter。View层主要有两个任务。1.对外提供修改控件属性的接口。2.响应外部事件,交给Presenter处理。这样Activity的任务就很明确了。
- Model的职责:提供给Presenter需要的数据。
四、实例.
一个登陆的例子(项目代码在这里下载:https://github.com/gatsbydhn/androidmvp):
View层:
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:
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 }
分析:登陆应该要从数据库获取数据验证账号密码是否正确,这里省略了这一步骤,但是要明白,这一层的职责是提供数据,不管是从数据库还是网络。