1.因为之前的开发用的都是mvc模式,就是说页面和处理逻辑都是写在了一起,MVP框架个人的理解不是很透彻,所以就从GitHub上面找了一个MVP的demo,链接的下载地址是https://github.com/antoniolg/androidmvp,写一下个人的理解心得,纯属记录。
笼统来讲,mvp大家从百度都能看到的定义就是
话说起来很简单,但是理解的时候我们还是需要一些项目进行实际的理解,话不多说,先看一下项目结构图
这个demo的接口很简单,就是涉及到两个页面,一个是login的页面还有一个是Main页面,实现的功能也很简单,就是Login页面输入账号名和密码之后(只要不为空就可以),就跳到Main页面.
先不看他这个结构,我们先用MVC 的思考方式想一下这个问题——如果是MVC模式,就是在Activity中进行两个EditText是否为空的判断,如果不为空就登录跳转,中间跳转的过程加载一个进度条的显示和隐藏的控制逻辑。
但是既然是MVP肯定是不一样的处理方式,上图中的java结构图已经可以看见一些端倪。
LoginActivity页面如下图:
我们按顺序的方式LoginACtivity开始讲起,先看我们最熟悉的LoginActivity页面
public class LoginActivity extends AppCompatActivity implements LoginView {
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 = findViewById(R.id.progress);
username = findViewById(R.id.username);
password = findViewById(R.id.password);
findViewById(R.id.button).setOnClickListener(v -> validateCredentials());
presenter = new LoginPresenter(this, new LoginInteractor());
}
@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 navigateToHome() {
startActivity(new Intent(this, MainActivity.class));
finish();
}
private void validateCredentials() {
presenter.validateCredentials(username.getText().toString(), password.getText().toString());
}
}
跟我们之前处理页面的逻辑不同之处在哪儿呢?
onCreate()函数里面有一个LoginPresenet类的对象,正常的登录按钮涉及到的处理逻辑的东西我们放到了presenter这个对象的中处理,除此以外呢,这个类实现了一个LoginView的接口,其实从名字里面我们就可以看出来,loginView就是MVC中的View的体现,我们代开代码页面看看这个接口中做了什么?
public interface LoginView {
void showProgress();
void hideProgress();
void setUsernameError();
void setPasswordError();
void navigateToHome();
}
接口的里面东西看名字都知道了,就是一个登录页面的时候需要用到的一些页面控件相关的方法:比如显示进度条(和当前progress控件有关)和跳珠到主页面(和控件login有关)
我们回到Activity中可以看到是在主页面中实现LoginVIew接口方法的。
但是你会发现一个问题是:这个接口实现的方法并没有在这个Activity中生命周期中调用,、
那,我们是在哪里调用的这些方法的呢?
我们回看在onCreate()方法中,可以看到Presenter这个对象初始化的时候第一个参数就是我们当前的LoginView,我们可以猜测就是在Present中用到了当前接口的方法
来看看这个LoginPresenter,上代码
LoginPresenter.java
public class LoginPresenter implements LoginInteractor.OnLoginFinishedListener {
private LoginView loginView;
private LoginInteractor loginInteractor;
LoginPresenter(LoginView loginView, LoginInteractor loginInteractor) {
this.loginView = loginView;
this.loginInteractor = loginInteractor;
}
public void validateCredentials(String username, String password) {
if (loginView != null) {
loginView.showProgress();
}
loginInteractor.login(username, password, this);
}
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.navigateToHome();
}
}
}
validateCredentials这个方法看上去很熟悉,如果看的仔细的同学应该记得Activity中通过Presenter对象在登录按钮触发函数中调用了这个方法,这个Activity的核心功能就在这个方法里面实现的,这个方法
MainActivity.java
public void validateCredentials(String username, String password) {
if (loginView != null) {
loginView.showProgress();
}
loginInteractor.login(username, password, this);
}
这个方法里面调用了loginInteractor这个接口的登录方法,同时在类中还实现了这个接口的三个方法,我们看看这个loginInteractor接口的代码,它做了什么
LoginInteractor.java
public class LoginInteractor {
interface OnLoginFinishedListener {
void onUsernameError();
void onPasswordError();
void onSuccess();
}
public void login(final String username, final String password, final OnLoginFinishedListener listener) {
// Mock login. I'm creating a handler to delay the answer a couple of seconds
new Handler().postDelayed(() -> {
if (TextUtils.isEmpty(username)) {
listener.onUsernameError();
return;
}
if (TextUtils.isEmpty(password)) {
listener.onPasswordError();
return;
}
listener.onSuccess();
}, 2000);
}
核心方法中的几种登录失败情况(密码和用户名为空)以及登录成功的情况都是调用了他自身的OnLoginFinishedListener这个接口中的三个方法,仔细想想,其实是在LoginPresenter中实现的这个三个方法,这三个方法 如示例 所示,都涉及了LoginView
的处理,最终的实现也是在Presenter这个类里面。
@Override
public void onUsernameError() {
if (loginView != null) {
loginView.setUsernameError();
loginView.hideProgress();
}
}
整个过程因为是由上到下说的,有点绕,我们正向梳理一遍。
1.我们其实就可以感觉到MVP处理逻辑的一种用法,涉及到view,本例子中是进度条显示和隐藏,就放到view接口中,在Activity中绑定相关控件的ID,实现I对应的View接口中的方法。
2.涉及到最具体的处理的逻辑放在另外的一个类就是LoginInterctor(从名字上理解感觉是一种拦截器的命名方式,第二张接口图中MainActivity页面也是),这类中首先放的是一个接口OnLoginFinishedListener,剩下的放核心方法,核心方法login()调用自己的内部接口OnLoginFinishedListene具体的不同情况下方法。
3.最后回到Presenter中将view和interactor建立联系——构造函数就是,有了相关的view,剩下就是可以在这个类中实现interactotr类的接口中具体实现方法,核心方法validateCredentials()中调用interactor类中的核心方法login()就行。
4.最后,回到Activity中,我们只需要完成一个presenter的实例化过程,调用它最后的方法,就能实现MVP方式管理一个Activity
MainActivity中的道理一样,感兴趣的同学可以下载看看,可以加深理解。
https://github.com/antoniolg/androidmvp