Android MVP模式
- View 对应于Activity,负责View的绘制以及与用户交互
- Model 依然是业务逻辑和实体模型
- Presenter 负责完成View于Model间的交互
mvp模式简化的Activity的代码逻辑,将复杂的逻辑代码提取到了Presenter中进行处理。对应的耦合度降低。
一张图总结Mvc和Mvp的区别
其实最明显的区别就是,MVC中中Model和View直接交互的,而MVP中很明显,Model与View之间的交互由Presenter完成,Presenter与View之间的交互是通过接口的。
Android Demo
需求:页面上有一个按钮,点击后去请求数据,成果后改变页面显示。
不使用mvp:
public class NotMvpActivity extends AppCompatActivity {
private Button bt;
private TextView tv;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_demo);
bt = (Button) findViewById(R.id.bt);
tv = (TextView) findViewById(R.id.tv);
bt.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
requestData();
}
});
}
/**
* 获取数据
*/
private void requestData() {
tv.setText("加载中");
new Thread() {
@Override
public void run() {
super.run();
SystemClock.sleep(2000);
runOnUiThread(new Runnable() {
@Override
public void run() {
tv.setText("成功获取到数据");
}
});
}
}.start();
}
}
以上应该是最原始的方法,下面使用mvp改造。
步骤:
1、创建接口,定义页面可能会出现的情况(加载中,成功,失败)。
2、Activity实现接口,成功View层。
3、创建一个类,实现网络请求的操作,Model层。
4、创建一个类,处理Model和View之间的通信,Presenter层。
1、view层的接口:
/**
* @project:MvpDemo
* @author:小卷子
* @date 2018/6/29
* @describe:View层接口 定义页面可能会出现的情况(加载中,成功,失败)
* @fix:
*/
public interface DemoView {
/**
* 加载中
*/
void Loading();
/**
* 获取成功
*/
void getDataSuccess();
/**
* 获取失败
*/
void getDataFail();
}
2、让Activity实现这个接口中的方法
public class DemoAcitivity extends AppCompatActivity implements DemoView {
private Button bt;
private TextView tv;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_demo);
bt = (Button) findViewById(R.id.bt);
tv = (TextView) findViewById(R.id.tv);
}
@Override
public void Loading() {
tv.setText("加载中");
}
@Override
public void getDataSuccess() {
tv.setText("成功");
}
@Override
public void getDataFail() {
tv.setText("失败");
}
}
3、创建一个类,用来封装之前的网络请求。(DemoModelCallBack是网络请求的回调,demo只是简单模拟)
/**
* @project:MvpDemo
* @author:小卷子
* @date 2018/6/29
* @describe:Mode1层 请求数据
* @fix:
*/
public class DemoMode1 {
public void getData(final DemoModelCallBack callBack) {
new Thread() {
@Override
public void run() {
super.run();
SystemClock.sleep(2000);
if (callBack != null) {
callBack.getDataSuccess();
}
}
}.start();
}
}
4、处理Model和View之间的通信,Presenter
/**
* @project:MvpDemo
* @author:小卷子
* @date 2018/6/29
* @describe:Presenter处理v和m的通信
* @fix:
*/
public class DemoPresenter {
private DemoModel demoModel;
private DemoView demoView;
public DemoPresenter(DemoModel demoModel, DemoView demoView) {
this.demoModel = demoModel;
this.demoView = demoView;
}
/**
* 请求数据
*/
public void getData() {
//view处理请求中
demoView.Loading();
//去请求数据
demoMode1.getData(new DemoModelCallBack() {
@Override
public void getDataSuccess() {
demoView.getDataSuccess();
}
@Override
public void getDataFail() {
demoView.getDataFail();
}
});
}
}
5、最后调整一下Activity ,Activity处理需要实现View层的接口,还要在自身创建Persenter。Activity接收到用户的事件时全部交给Persenter来处理。Persenter处理事件后将结果反馈到View(也就是Activity)。所以思路大概是这样:
- Activity需要实现View层接口
- Persenter需要持有View层引用和Model层引用
- 在Activity中创建Persenter,通过Persenter连接自身和Model
调整后的Activity
/**
* @project:MvpDemo
* @author:小卷子
* @date 2018/6/29
* @describe:Activity实现view接口
* @fix:
*
*/
public class DemoAcitivity extends AppCompatActivity implements DemoView {
private Button bt;
private TextView tv;
private DemoPresenter demoPresenter;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_demo);
bt = (Button) findViewById(R.id.bt);
tv = (TextView) findViewById(R.id.tv);
demoPresenter = new DemoPresenter(this);
bt.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//点击事件交给 demoPresenter处理
demoPresenter.getData();
}
});
}
@Override
public void Loading() {
runOnUiThread(new Runnable() {
@Override
public void run() {
tv.setText("加载中");
}
});
}
@Override
public void getDataSuccess() {
runOnUiThread(new Runnable() {
@Override
public void run() {
tv.setText("成功");
}
});
}
@Override
public void getDataFail() {
runOnUiThread(new Runnable() {
@Override
public void run() {
tv.setText("失败");
}
});
}
}
简单的Demo模式的MVP架构存在很多问题不能应用到实际开发中。(代码冗余,复用性差)。所以针对这些问题需要进行优化。
调用View可能引发的空指针异常
以上例子中请求网络数据等待反馈后跟新到页面,但是在请求过程中当前的View可能被销毁,Presenter在网络回调后调用View更新就会引发空指针。要避免这种情况就要在每次调用View的时候都需要知道View是否存在。也就是Presenter必须知道Activity的生命状态。
Presenter中新增:
/**
* 绑定
*
* @param demoView
*/
public void attachView(DemoView demoView) {
this.demoView = demoView;
this.demoModel = new DemoModel();
}
/**
* 解除绑定
*/
public void detachView() {
this.demoView = null;
//todo DemoModel取消网络请求
}
/**
* 当前是否与View建立连接
* 每次调用view的时候先调用此方法检查
* @return
*/
public boolean isViewAttached() {
return demoView != null;
}
-
attachView()
绑定View引用。 -
detachView
断开View引用。 -
isViewAttached()
判断View引用是否存在。
Presenter与View的绑定不在通过构造传入,通过attachView()
和detachView
绑定生命周期,每次调用View之前判断是否存在。