重拾Android之路(十七)MVP
关于MVP,MVVM,早就烂大街了,说出来可能都不要意思,在实际项目中,我没有将这两个内容使用的非常熟练,因为在项目中,一般都是一些比较小型的项目,真的用这些模式的地方并不多。但是最近几天在GitHub上面看到一个不错的共享程序–头条。感觉各方面都挺好的,所以就在闲暇的时间仔细研究了一下,发现了很多值得学习的地方,那么记录一下上面用到的技术,自己也要好好的学习,这是地址仿今日头条
今天来说一说MVP模式
概述
为什么使用MVP模式
可能有些人从一开始学习编程,到现在还没有使用过这些诸如MVP,MVVM的模式,大部分使用的是MVC,这是因为一般情况下,MVC的模式更容易理解,在代码量非常大的时候,如果贸然的改到MVP,是需要非常多的工作量的。但是,一个思想的提出肯定有他的过人之处,那么MVP的过人之处就是层次清晰。
举个例子在我们洗衣服的时候,我们肯定会将袜子和内衣分开来洗,因为如果一起洗的话,不卫生,那么我们可以认为,在需要洗袜子的时候,只关注如何洗袜子就可以了,而当我们洗内衣的时候,只需要关注内衣就可以,在某一个阶段只关注某一个方面。这样比较有条理。其实MVP模式也是一样,在我们程序员看来,如果能够很好的将数据操作,交互,用户界面完全的分离,那么我们在编写代码的时候,是非常轻松的,而且,如果某一个功能出现了问题,直接找到相应的模块即可。
MVP也有自己的缺点,这个缺点,我现在这里说一下,后面在实际使用中,会在演示,就是当程序慢慢增加的时候,代码量会超级多,文件也会更多。
逻辑思路图
这个图是我从网上找的,这里是原图和原文的地址android MVP框架搭建
MVP模式分为三层:
Activity和Fragment视为View层,负责和用户的交互
Presenter为业务逻辑层,技能调用UI逻辑,又能请求数据,该层应该有纯Java代码实现,不应该涉及Android API
Model为数据层,包括数据请求,数据源操作等内容
这里View和Callback都是接口,两者的不同之处在于Callback是用于数据操作的接口,如数据返回等,View是一些跟UI界面相关的,如按钮点击事件的操作等。
说了这么多,其实都不如直接上代码来的实在,一个例子,模拟网络请求
先看效果,我做的GIF很难看,凑合吧
Model层
model层主要用来模拟网络请求,这里使用handler的方式,延迟3秒钟,这里面有一个callback的对象,用来实现model和presenter的交互,就是这个callback会通知presenter中特定的方法,去执行相应的操作
public class MVPModel {
/**
* 获取网络数据
*
* @param param
* @param callback
*/
public static void getNetData(final String param, final BaseCallback callback) {
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
switch (param) {
case "normal":
callback.onSuccess("请求数据成功");
break;
case "failure":
callback.onFailure("请求数据失败");
break;
case "error":
callback.onError();
break;
}
callback.onComplete();
}
}, 3000);
}
}
Presenter层
具体业务逻辑层,主要用来发送网络请求,并且会对请求的返回进行处理,并且会通知activity进行相应的UI改变。这里MVPCallback就是在Model中的那个Callback对象,Model中的数据进行操作之后,会触发相应的方法在Presenter中执行。而在Presenter中,我们生命了MVPView接口对象,这个对象就是用来实现Presenter层和Activity进行交互的接口对象。
public class MVPPersenter implements MVPCallback<String> {
private MVPView mvpView;
public MVPPersenter() {
}
/**
* 情况:如果我们正在进行网络请求,而这时我们的Activity被销毁了,那么会出现空指针异常的情况,为了解决和避免这一情况的发生,需要使用绑定
*/
public void attachView(MVPView mvpView) {
this.mvpView = mvpView;
}
public void detachView() {
this.mvpView = null;
}
public boolean isViewAttach() {
return this.mvpView != null;
}
/**
* 获取网络请求数据
*/
public void getData(String param) {
//打开进度条
mvpView.showLoading();
MVPModel.getNetData(param, this);
}
@Override
public void onSuccess(String data) {
if (isViewAttach()) {
mvpView.showData(data);
}
}
@Override
public void onFailure(String msg) {
if (isViewAttach()) {
mvpView.showFailureMsg(msg);
}
}
@Override
public void onError() {
if (isViewAttach()) {
mvpView.showError();
}
}
@Override
public void onComplete() {
if (isViewAttach()) {
mvpView.hideLoading();
}
}
}
View层
这里我们直接使用的是Activity表示,这里我们需要让Activity实现MVPView接口和声明Presenter对象,这样,当presenter对象接收到相应的操作的时候,就通知Activity进行相应的UI操作
public class MainActivity extends AppCompatActivity implements MVPView{
private Button button;
private TextView textView;
private MVPPersenter mvpPersenter;
private ProgressDialog dialog;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mvpPersenter = new MVPPersenter();
mvpPersenter.attachView(this);
button = findViewById(R.id.btn_1);
textView = findViewById(R.id.msg);
//初始化进度条
dialog = new ProgressDialog(this);
dialog.setCancelable(false);//不能自动关闭
dialog.setMessage("正在加载");
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
mvpPersenter.getData("normal");
}
});
}
@Override
public void showLoading() {
if(!dialog.isShowing()){
dialog.show();
}
}
@Override
public void hideLoading() {
if (dialog.isShowing()){
dialog.dismiss();
}
}
@Override
public void showData(String data) {
textView.setText(data);
}
@Override
public void showFailureMsg(String msg) {
textView.setText(msg);
}
@Override
public void showError() {
Toast.makeText(this, "网络请求数据出现异常", Toast.LENGTH_SHORT).show();
textView.setText("网络请求数据出现异常");
}
@Override
protected void onDestroy() {
super.onDestroy();
mvpPersenter.detachView();
}
}
MVPView接口
这是一个接口,完成View和Presenter之间的数据交互
public class MainActivity extends AppCompatActivity implements MVPView{
private Button button;
private TextView textView;
private MVPPersenter mvpPersenter;
private ProgressDialog dialog;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mvpPersenter = new MVPPersenter();
mvpPersenter.attachView(this);
button = findViewById(R.id.btn_1);
textView = findViewById(R.id.msg);
//初始化进度条
dialog = new ProgressDialog(this);
dialog.setCancelable(false);//不能自动关闭
dialog.setMessage("正在加载");
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
mvpPersenter.getData("normal");
}
});
}
@Override
public void showLoading() {
if(!dialog.isShowing()){
dialog.show();
}
}
@Override
public void hideLoading() {
if (dialog.isShowing()){
dialog.dismiss();
}
}
@Override
public void showData(String data) {
textView.setText(data);
}
@Override
public void showFailureMsg(String msg) {
textView.setText(msg);
}
@Override
public void showError() {
Toast.makeText(this, "网络请求数据出现异常", Toast.LENGTH_SHORT).show();
textView.setText("网络请求数据出现异常");
}
@Override
protected void onDestroy() {
super.onDestroy();
mvpPersenter.detachView();
}
}
MVPCallback接口
实现Model和Presenter的交互
public interface MVPCallback<T> {
/**
* 请求成功
* @param data
*/
void onSuccess(T data);
/**
* 请求失败
* @param msg
*/
void onFailure(String msg);
/**
* 请求数据失败,一边表现在请求数据方式不对,网络权限失败,内存泄漏等原因无法连接到请求数据源
*/
void onError();
/**
* 请求完成,不管请求是成功,失败,还是异常,都会执行完成操作
*/
void onComplete();
}
总结
这就是最简单的一个关于MVP的例子,但是这里我们存在很多问题
- 框架存在漏洞
- 代码冗余
- 代码重构性差
我们需要对这个内容进行新一轮的修改,那么为了解决代码重构的问题,我们一般都是依赖于继承的方式解决,那么就是给我们需要的操作写一些父类
BaseCallback
通过泛型,让我们可以传递不同类型的数据
public interface BaseCallback<T> {
void onSuccess(T data);
void onFailure(String msg);
void onError();
void onComplete();
}
BasePresenter
有些时候在我们执行网络请求时,如果突然App异常退出,会导致数据请求一半,然后没有响应了。那么我们需要将View接口和Activity进行绑定
public class BasePresenter <V extends BaseView>{
private V view;
public void attachView(V view){
this.view = view;
}
public void detachView(){
this.view = null;
}
public boolean isAttachView(){
return this.view != null;
}
public V getView(){
return this.view;
}
}
BaseView
View接口的基类
public interface BaseView {
void showLoading();
void hideLoading();
void showToast(String msg);
void showError();
Context getContext();
}
View接口的子类
public interface SecondView extends BaseView {
void showData(String data);
}
BaseActivity
在BaseActivity中我们需要实现BaseVIew接口,并且需要声明进度条
public abstract class BaseActivity extends AppCompatActivity implements BaseView {
//声明一个加载进度狂
private ProgressDialog dialog;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
dialog = new ProgressDialog(this);
dialog.setCancelable(false);
}
@Override
public void showLoading() {
if (!dialog.isShowing()){
dialog.show();
}
}
@Override
public void hideLoading() {
if (dialog.isShowing()){
dialog.dismiss();
}
}
@Override
public void showToast(String msg) {
Toast.makeText(this,msg,Toast.LENGTH_LONG).show();
}
@Override
public void showError() {
showToast("请求失败");
}
@Override
public Context getContext() {
return BaseActivity.this;
}
}
BasePresenter的子类
public class SecondPresenter extends BasePresenter<SecondView> {
public void getData(String param) {
if (!isAttachView()) {
return;
}
getView().showLoading();
MVPModel.getNetData(param, new BaseCallback<String>() {
@Override
public void onSuccess(String data) {
getView().showData(data);
}
@Override
public void onFailure(String msg) {
getView().showToast(msg);
}
@Override
public void onError() {
getView().showError();
}
@Override
public void onComplete() {
getView().hideLoading();
}
});
}
}
BaseActivity的子类
public class SecondActivity extends BaseActivity implements SecondView {
private Button btn;
private TextView textView;
private SecondPresenter persenter;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btn = findViewById(R.id.btn_1);
textView = findViewById(R.id.msg);
persenter = new SecondPresenter();
persenter.attachView(this);
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
persenter.getData("normal");
}
});
}
@Override
public void showData(String data) {
textView.setText(data);
}
@Override
protected void onDestroy() {
super.onDestroy();
persenter.detachView();
}
}
Model
不需要改变
public class MVPModel {
/**
* 获取网络数据
*
* @param param
* @param callback
*/
public static void getNetData(final String param, final BaseCallback callback) {
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
switch (param) {
case "normal":
callback.onSuccess("请求数据成功");
break;
case "failure":
callback.onFailure("请求数据失败");
break;
case "error":
callback.onError();
break;
}
callback.onComplete();
}
}, 3000);
}
}
MVPDemo例子
除了这个之外,我又写了一个关于登录的MVPdemo,也把代码贴出来,都是比较基础的内容登录功能的MVP实现
上一篇: MVP andorid 使用
下一篇: Android 自定义Log 多模式