MVP模式
简称:MVP 全称:Model-View-Presenter ;MVP 是从经典的模式MVC演变而来,它们的基本思想有相通的地方:Controller/Presenter负责逻辑的处理,Model提供数据,View负责显示。
这张图可以很清晰的看出MVP各层的职责,简单来说
M层,即Model数据模型层,主要用来提供数据
V层,即VIew视图层,用来展示视图-------由Activity充当
P层,即preserter逻辑层,他是VIew层和Model层的桥梁,也是MVP模式的特点
MVP模式有哪些优点呢
MVP模式是由MVC模式演化而来,在MVC模式中,Activity即充当了View又充当了Controller,这样就导致Activity中的代码可能会非常的臃肿,MVP模式就很好的解决了这个问题。
具体的优点:
1、模型与视图完全分离,我们可以修改视图而不影响模型
2、可以更高效地使用模型,因为所有的交互都发生在一个地方--Presenter内部
3、我们可以将一个Presenter用于多个视图,而不需要改变Presenter的逻辑。这个特性非常的有用,因为视图的变化总是比模型的变化频繁。
4、如果我们把逻辑放在Presenter中,那么我们就可以脱离用户接口来测试这些逻辑(单元测试)
如何使用MVP
既然MVP模式这么好用,那我们应该如何使用呢,接下来通过一个用户登录的demo来解析MVP模式
项目分包:
这里主要分成了di和ui两个包,di处理数据逻辑即M层和P层,ui处理页面即V层。
创建契约类
我个人认为创建契约类的方法非常好,帮助我们管理MVP中的接口,一目了然。
契约类的代码如下
public interface IContract {
public interface IView{
void ShowData(String msg);
}
public interface IPresenter<IView>{
void resqusetMesg(String name,String pwd);
void attachView(IView iView);
void deattachView(IView iView);
}
public interface IModel{
public interface CallBack{
void responseData(String msg);
}
void requestData(String name,String pwd,CallBack callBack);
}
}
非常容易可以看出,这一个接口托管了MVP三层的接口
Activity中
刚才已经介绍过了,在MVP中Acticity主要负责的是view的操作
所以我们的Acticity需要实现View的接口,重写View中的方法
到这里Activity如下
public class MainActivity extends AppCompatActivity implements IContract.IView {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
@Override
public void ShowData(String msg) {
}
之后我们的view层应该与p层进行关联,也就是我们的Activity中需要持有p层的引用
private IContract.IPresenter presenter;
刚刚说过,p层主要是逻辑层,处理View层的数据
所以当我们点击按钮之后,应该把输入框中的内容传到P层中进行非空判断或者其他的逻辑判断
而且P层要和View进行关联,所以在持有P层的引用之后,应该调用P层的方法与View进行关联
//绑定当前的视图
presenter.attachView(this);
所以onCreate中如下
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
presenter = new PresenterImpl();
//绑定当前的视图
presenter.attachView(this);
name = findViewById(R.id.text_name);
pwd = findViewById(R.id.text_pwd);
button = findViewById(R.id.btn_login);
//点击按钮获取文本内容
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String name = MainActivity.this.name.getText().toString();
String pwd = MainActivity.this.pwd.getText().toString();
//传到P层
presenter.resqusetMesg(name,pwd);
}
});
}
之后我们再看重写的方法
Activity是负责View的,也就是UI,重写的方法也就是更新UI的方法
所以我们重写的方法中代码如下
@Override
public void ShowData(final String msg) {
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(MainActivity.this,msg,Toast.LENGTH_SHORT).show();
}
});
}
之后我们重写onDestroy方法,销毁视图
@Override
protected void onDestroy() {
presenter.deattachView(this);
super.onDestroy();
}
到这里Activity中的所有代码就完成了
Activity中的所有代码如下:
public class MainActivity extends AppCompatActivity implements IContract.IView {
private IContract.IPresenter presenter;
private EditText name;
private EditText pwd;
private Button button;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
presenter = new PresenterImpl();
presenter.attachView(this);
name = findViewById(R.id.text_name);
pwd = findViewById(R.id.text_pwd);
button = findViewById(R.id.btn_login);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String name = MainActivity.this.name.getText().toString();
String pwd = MainActivity.this.pwd.getText().toString();
presenter.resqusetMesg(name,pwd);
}
});
}
@Override
public void ShowData(final String msg) {
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(MainActivity.this,msg,Toast.LENGTH_SHORT).show();
}
});
}
@Override
protected void onDestroy() {
presenter.deattachView(this);
super.onDestroy();
}
}
P层的实现类
首先先实现P层的接口,重写里面的方法
public class PresenterImpl implements IContract.IPresenter<IContract.IView> {
@Override
public void resqusetMesg(String name, String pwd) {
}
@Override
public void attachView(IContract.IView iView) {
}
@Override
public void deattachView(IContract.IView iView) {
}
}
P层就是view层和model层的一个桥梁
所以在P层中需要持有VIew层和Model层的引用,并且定义一个弱引用防止内存泄漏
private IContract.IView iView;
private IContract.IModel iModel;
private WeakReference<IContract.IView> iViewWeakReference;
private WeakReference<IContract.IModel> iModelWeakReference;
接下来,看一下重写的方法的作用
首先 resqusetMesg 方法,用来接收View传过来的数据,并进行逻辑操作
这里主要思路是先进行非空判断,如果不为空的话到Model层中去请求数据,然后通过接口回调把结果返回到View层
所以这个方法中的代码如下
@Override
public void resqusetMesg(String name, String pwd) {
if (name !=null){
iModel.requestData(name, pwd, new IContract.IModel.CallBack() {
@Override
public void responseData(String msg) {
iView.ShowData(msg);
}
});
}
}
接下来 attachView 方法
这个方法的主要作用就是让P层和VIew层建立联系
代码如下:
@Override
public void attachView(IContract.IView iView) {
this.iView = iView;
iModel = new ModelImpl();
iModelWeakReference = new WeakReference<>(iModel);
iViewWeakReference = new WeakReference<>(iView);
}
最后 deattachView 方法
显而易见也就是销毁View的方法
@Override
public void deattachView(IContract.IView iView) {
iViewWeakReference.clear();
iModelWeakReference.clear();
}
至此P层就全部写完了
P层的全部代码如下 :
public class PresenterImpl implements IContract.IPresenter<IContract.IView> {
private IContract.IView iView;
private IContract.IModel iModel;
private WeakReference<IContract.IView> iViewWeakReference;
private WeakReference<IContract.IModel> iModelWeakReference;
@Override
public void resqusetMesg(String name, String pwd) {
if (name !=null){
iModel.requestData(name, pwd, new IContract.IModel.CallBack() {
@Override
public void responseData(String msg) {
iView.ShowData(msg);
}
});
}
}
@Override
public void attachView(IContract.IView iView) {
this.iView = iView;
iModel = new ModelImpl();
iModelWeakReference = new WeakReference<>(iModel);
iViewWeakReference = new WeakReference<>(iView);
}
@Override
public void deattachView(IContract.IView iView) {
iViewWeakReference.clear();
iModelWeakReference.clear();
}
}
M层的实现类
Model层是数据模型层,也就是主要负责请求数据的
首先先实现Model层的接口,重写里面的方法
public class ModelImpl implements IContract.IModel {
@Override
public void requestData(String name, String pwd, CallBack callBack) {
}
}
之后我们通过OKHttp访问网络数据,也可以根据需求选择其他的网络请求框架
通过post请求,将我们拿到的用户名和密码到网上进行查询,并将查询到的结果返回
Model层的所有代码如下 :
public class ModelImpl implements IContract.IModel {
@Override
public void requestData(String name, String pwd, final CallBack callBack) {
String url = "https://www.zhaoapi.cn/user/login";
OkHttpClient okHttpClient = new OkHttpClient
.Builder()
.writeTimeout(15, TimeUnit.SECONDS)
.readTimeout(15, TimeUnit.SECONDS)
.build();
FormBody build = new FormBody
.Builder()
.add("mobile", name)
.add("password", pwd)
.build();
Request request = new Request.Builder().url(url).post(build).build();
okHttpClient.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
}
@Override
public void onResponse(Call call, Response response) throws IOException {
String msg = response.body().string();
callBack.responseData(msg);
}
});
}
}
思路整理
1.首先先写一个契约类,管理MVP的全部接口
2.让MainActivity继承View的接口,重写方法
3.MainActivity持有P层的引用,点击按钮把获取到的内容传到P层进行逻辑操作
4.写P层的实现类,判断内容,如果没有问题把数据传到M层
5.写M层的实现类,主要进行请求数据的操作
6.请求成功,把结果返回View层展示
注意:内存泄漏的问题
希望这篇文章对你的MVP学习有所帮助