
MVP简介
随着时代的发展,移动端UI越来越丰富、业务越来越复杂,用户对界面的美观、渲染的速度、数据读取的速度要求也越来越高。为了在保证拥有酷炫的UI、复杂的业务逻辑的条件下给用户更好的体验,需要对view、model、controller职责进行细分,让view专注于处理数据的可视化以及和用户的交互、让model专注于数据的处理。一种新的模式MVP(Model - View - Presenter)应运而生。
MVP和MVC的差异
MVP模式是从MVC模式理念的基础上衍生出来的,在MVC的基础上对V和M进行了绝对的解耦,两者之间只有通过Presenter才能通信,优化了V层和C层,职责划分更明确、单一。MVC中,M层和V层是可以通信的,MVP中M层和V层是完全隔离的,只能通过P层来通信,如图(这里借图一用)。

在MVC模式时期,由于UI越来越丰富、逻辑处理越来越复杂,Activity中对UI的活动展示以及和用户的交互也越来越复杂,代码量比较大,同时受限于Android的线程安全以及很多时候需要使用到Activity的生命周期,我们有不少的Controller层的代码也会写在Activity里面,也就是Activity同时承担了View层和部分Controller层的工作,影响开发效率,不利于维护和扩展。
于是,我们将复杂的逻辑移至另外一个类Presenter,让Activity只负责UI的展示以及和用户的交互。View层,创建View Interface,定义好View层的活动事件,Activity加载布局、实现View Interface,将View Interface传入Presenter实例,通过View Innterface和Presenter实现交互;Model层,负责存储、检索、操纵数据,可以通过创建一个Model Interface来进一步实现解耦;Presenter层,作为View和Model交互的中间枢纽,负责接收用户操作,调用Model层,将数据反馈到View层展示给用户。
选择MVP的必要性
1)降低了耦合性;
2)各模块职责划分明确,利于协同开发;
3)业务逻辑独立出来,通过接口调用,方便单元测试;
4)代码重用性高;
示例
这里跟随很多大牛的脚步,同样使用登录页面来作为第一次体验MVP的示例。
①登录页面布局activity_main.xml代码,布局比较简单,两个输入框,一个登录按钮,一个加载等待条(默认隐藏)。
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".LoginActivity"
android:padding="20dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_centerVertical="true"
>
<EditText
android:id="@+id/userName"
android:layout_width="match_parent"
android:layout_height="50dp"
android:hint="用户名"/>
<EditText
android:id="@+id/password"
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_marginTop="5dp"
android:hint="密码"/>
<Button
android:id="@+id/loginBtn"
android:layout_width="match_parent"
android:layout_height="60dp"
android:layout_marginTop="10dp"
android:text="Login"/>
</LinearLayout>
<ProgressBar
android:id="@+id/progressBar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:visibility="gone"/>
</RelativeLayout>
②View Interface,这里定义的是View的活动,这里登录页面存在的活动有(显示加载等待框、隐藏加载等待框、返回登录成功或者登录失败),所以ILoginView代码如下。
public interface ILoginView {
/*显示等待框*/
void showProgress();
/*隐藏等待框*/
void hideProgress();
/*登录成功*/
void loginSuccess();
/*登录失败*/
void loginError();
}
③Model Interface,需要在model中去判断用户名和密码是否正确,并返回处理结果,所以ILoginModel需要定义一个checkLogin方法,再定义一个回调的内部接口,因为只有P能主动通信M,M不能主动通信P,所有这里采用回调来返回结果。
public interface ILoginModel {
/*回调接口*/
interface OnLoginListener{
/*登录成功回调*/
void loginSuccess();
/*登录失败回调*/
void loginError();
}
/*判断输入的用户名、密码,调用内部接口回调*/
void checkLogin(String userName,String password,OnLoginListener listener);
}
④实现ILoginModel,这里虚拟登录。
public class LoginModelImpl implements ILoginModel {
@Override
public void checkLogin(final String userName, final String password,
final OnLoginListener listener) {
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
if(userName.equals("1") && password.equals("1")){
listener.loginSuccess();
}else{
listener.loginError();
}
}
},3000);
}
}
⑤Presenter Interface,Presenter接收用户点击按钮的事件,然后调用Model处理数据,ILoginPresenter需要toLogin方法。
public interface ILoginPresenter {
void toLogin(String userName,String password);
}
⑥实现Presenter,P层作为V和M的交互媒介,需要同时持有V和M,在M处理完返回的时候还需要通知到V层,所以还要实现M层的回调接口。由于V层可以主动沟通P层,所以V层和P层互相持有,这里V层直接在构造函数里传递过来。
public class LoginPresenterImpl implements ILoginPresenter,
ILoginModel.OnLoginListener {
private ILoginView mView;
private ILoginModel mModel;
public LoginPresenterImpl(ILoginView iLoginView) {
mView = iLoginView;
mModel = new LoginModelImpl();
}
@Override
public void toLogin(String userName, String password) {
mView.showProgress();
mModel.checkLogin(userName,password,this);
}
@Override
public void loginSuccess() {
mView.hideProgress();
mView.loginSuccess();
}
@Override
public void loginError() {
mView.hideProgress();
mView.loginError();
}
}
⑦View实现加载,这里在Acitvity中操作,实现View Interface,实例化Presenter,将View Interface作为参数传递给Presenter。
package liu.wolf.firstmvpdemo;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ProgressBar;
import android.widget.Toast;
import liu.wolf.firstmvpdemo.presenter.LoginPresenterImpl;
import liu.wolf.firstmvpdemo.view.ILoginView;
public class LoginActivity extends AppCompatActivity implements
ILoginView{
private ProgressBar progressBar;
private EditText userName,password;
private Button loginBtn;
private LoginPresenterImpl mPresenter = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mPresenter = new LoginPresenterImpl(this);
progressBar = findViewById(R.id.progressBar);
userName = findViewById(R.id.userName);
password = findViewById(R.id.password);
loginBtn = findViewById(R.id.loginBtn);
loginBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mPresenter.toLogin(userName.getText().toString(),
password.getText().toString());
}
});
}
@Override
public void showProgress() {
progressBar.setVisibility(View.VISIBLE);
}
@Override
public void hideProgress() {
progressBar.setVisibility(View.GONE);
}
@Override
public void loginSuccess() {
Toast.makeText(LoginActivity.this,"登录成功!",
Toast.LENGTH_LONG).show();
}
@Override
public void loginError() {
Toast.makeText(LoginActivity.this,"登录失败!",
Toast.LENGTH_LONG).show();
}
}
代码下载:https://download.csdn.net/download/liujibin1836591303/10374061
阅读原文
网友评论