一、概述
对于MVP设计模式,我相信大部分安卓开发者都应该听过,可到底MVP是怎么样的,或许很多人也没有实践过,今天,我就带大家走进MVP的使用。
1、MVP是什么?
Model-view-presenter,简称MVP,是软件设计中一种对针对MVC模式,再审议后所延伸提出的一种软件设计模式。
MVP的使用有助于实现程序结构分离,解耦。
Model 定义程序的业务逻辑和实体模型。
View 视图层,用以展示来自Model的信息以及经过Presenter对事件处理后的信息,对应于布局文件、Activity和Fragment。
Presenter 包含着事件逻辑处理,负责从Model层获取信息,并将取得的信息经过格式转换与View进行通讯。
2、MVP与MVC的区别
MVC
Model:定义程序的业务逻辑和实体模型。
View:视图层,对应于布局文件。
Controller:业务逻辑层,主要是Activity和Fragment。
MVC里View对应于布局文件,其实做的事情仅仅是展示控件,实际上数据绑定的操作,事件处理的代码都在Activity或Fragment中,造成了Activity或Fragment既承担了View的功能又承担了Controller的功能,MVC还允许Model与View进行交互。
而在MVP中,Presenter的出现,将Actvity/Fragment作为View层,Presenter负责完成View层与Model层的交互,这样做的好处是程序代码耦合度更低,更容易测试,结构更清晰。但随之也带来一个问题:类的数量过多,为了实现一个界面功能,相比传统MVC,你可能需要创建更多额外的类文件。
二、案例
本案例,我实现了一个请求网络新闻的功能。下面来介绍MVP在其中的应用。
包结构:
(1)Model层
News实体类,我是通过GsonFormat生成的
public class News {
private String error_code;
private String reason;
private List<ResultBean> result;
public String getErrorCode() {
return error_code;
}
public void setErrorCode(String error_code) {
this.error_code = error_code;
}
public String getReason() {
return reason;
}
public void setReason(String reason) {
this.reason = reason;
}
public List<ResultBean> getResult() {
return result;
}
public void setResult(List<ResultBean> result) {
this.result = result;
}
public static class ResultBean {
private String title;
private String content;
private String img_width;
private String full_title;
private String pdate;
private String src;
private String img_length;
private String img;
private String url;
private String pdate_src;
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public String getImg_width() {
return img_width;
}
public void setImg_width(String img_width) {
this.img_width = img_width;
}
public String getFull_title() {
return full_title;
}
public void setFull_title(String full_title) {
this.full_title = full_title;
}
public String getPdate() {
return pdate;
}
public void setPdate(String pdate) {
this.pdate = pdate;
}
public String getSrc() {
return src;
}
public void setSrc(String src) {
this.src = src;
}
public String getImg_length() {
return img_length;
}
public void setImg_length(String img_length) {
this.img_length = img_length;
}
public String getImg() {
return img;
}
public void setImg(String img) {
this.img = img;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getPdate_src() {
return pdate_src;
}
public void setPdate_src(String pdate_src) {
this.pdate_src = pdate_src;
}
}
}
获取新闻的逻辑接口INewsDao:
public interface INewsDao {
void getNews(String key,OnDataGetListener listener);
}
public class NewsDaoImpl implements INewsDao {
public static final String BASE_URL = " http://apis.haoservice.com/lifeservice/news?";
public static final String APP_KEY = "6a74ccfde5e04f27b344718ba923fe3f";
/**
* 请求新闻数据的具体实现方法
* @param keyWords 关键词
* @param listener 请求结果回调接口
*/
@Override
public void getNews(String keyWords, final OnDataGetListener listener) {
try {
keyWords = URLEncoder.encode(keyWords, "utf-8");
StringBuilder sb = new StringBuilder();
sb.append(BASE_URL);
sb.append("key=");
sb.append(APP_KEY);
sb.append("&q=");
sb.append(keyWords);
//创建一个请求
final Request request = new Request.Builder().url(sb.toString()).build();
//创建一个Call
final Call call = HttpHelper.getInstance().getOkHttpClient().newCall(request);
//执行请求
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
if (listener != null) {
listener.onError();
}
}
@Override
public void onResponse(Call call, Response response) throws IOException {
if (listener != null) {
listener.onSuccess(response.body().string());
}
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
}
网络请求采用OkHttp:
public class HttpHelper {
private static HttpHelper mInstance;
private static OkHttpClient mOkHttpClient;
private HttpHelper() {
//初始化OkHttpClient
mOkHttpClient = new OkHttpClient().newBuilder()
.connectTimeout(10, TimeUnit.SECONDS)//设置超时时间
.readTimeout(10, TimeUnit.SECONDS)//设置读取超时时间
.writeTimeout(10, TimeUnit.SECONDS)//设置写入超时时间
.build();
}
public static HttpHelper getInstance() {
if (mInstance == null) {
synchronized (HttpHelper.class) {
if (mInstance == null) {
mInstance = new HttpHelper();
}
}
}
return mInstance;
}
public OkHttpClient getOkHttpClient() {
return mOkHttpClient;
}
}
(2)View层
Presenter与View交互是通过接口。所以我们这里需要定义一个INewsView,通过里面的接口方法来执行对View的操作。
/**
* 通用的View模块,可以在这里写一些公用的接口方法
* @author king
* @date 2017/3/10 10:23
*/
public interface IViewBinder {
}
//开发人员可以根据实际需求扩展该接口,增加其他操作View的方法。
public interface INewsView extends IViewBinder {
void showProgress();//显示进度条
void hideProgress();//隐藏进度条
void onSuccess(String response);//请求成功
void onError();//请求失败
}
作为View层,最重要的当然是Activity,MVP模式中Activity也是作为View层一部分。
public class MainActivity extends AppCompatActivity implements INewsView {
private ProgressDialog mProgressDialog;
private Button btn_get;
private TextView tv_data;
private NewsPresenter mNewsPresenter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btn_get = (Button) findViewById(R.id.btn_get);
tv_data = (TextView) findViewById(R.id.tv_data);
mProgressDialog = new ProgressDialog(this);//创建进度加载框
mProgressDialog.setMessage("加载中");
mNewsPresenter = new NewsPresenter(this);//创建Presenter
btn_get.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mNewsPresenter.getNews("三星电视变CIA窃听器");//触发网络请求
}
});
}
/**
* 显示加载进度框
*/
@Override
public void showProgress() {
mProgressDialog.show();
}
/**
* 隐藏加载进度框
*/
@Override
public void hideProgress() {
mProgressDialog.dismiss();
}
/**
* 请求数据成功回调
*/
@Override
public void onSuccess(String response) {
try {
News news = parseJson(response);
String code = news.getErrorCode();
if("0".equals(code)) {
String text = news.getResult().get(0).getTitle()+"\n\n"+news.getResult().get(0).getContent();
tv_data.setText(text);
}else{
Toast.makeText(this,news.getReason(),Toast.LENGTH_SHORT).show();
}
}catch (Exception e){
}
}
/**
* 请求数据失败回调
*/
@Override
public void onError() {
Toast.makeText(this,"请求数据失败",Toast.LENGTH_SHORT).show();
}
/**
* 解析json
*/
private News parseJson(String json) {
return new Gson().fromJson(json, News.class);
}
}
(3)Presenter
Presenter用于处理Model与View的交互,类似MVC中的Controller层,数据获取到了,就应该展示在View。
首先定义一个IBasePresenter用来封装一些通用的方法,例如关联Activity的生命周期,在各个方法用户可以自己定义自己想要做的事。
/**
* 基础的presenter,用于关联Activity或fragment生命周期
* @author king
* @date 2017/3/10 10:24
*/
public interface IBasePresenter {
void onCreate();
void onStart();
void onPause();
void onStop();
void onDestory();
}
public class NewsPresenter implements IBasePresenter{
private INewsView mNewsView;
private NewsDaoImpl newsDao;
private Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
switch (msg.what){
case 200:
mNewsView.onSuccess((String) msg.obj);//因为是异步,所以需要handler
break;
case 201:
mNewsView.onError();
break;
}
}
};
public NewsPresenter(INewsView view){
this.mNewsView = view;
this.newsDao = new NewsDaoImpl();
}
/**
* 获取新闻数据
* @param key
*/
public void getNews(String key){
mNewsView.showProgress();
newsDao.getNews(key, new OnDataGetListener() {
@Override
public void onSuccess(String response) {
mNewsView.hideProgress();
Message msg = Message.obtain();
msg.what = 200;
msg.obj = response;
mHandler.sendMessage(msg);
}
@Override
public void onError() {
mNewsView.hideProgress();
Message msg = Message.obtain();
msg.what = 201;
mHandler.sendMessage(msg);
}
});
}
@Override
public void onCreate() {
}
@Override
public void onStart() {
}
@Override
public void onPause() {
}
@Override
public void onStop() {
}
@Override
public void onDestory() {
}
}
注意到,Presenter里面有Model层和View层 的实现类:NewsDaoImpl和INewsView,这就是Presenter的作用,从View中获取需要的数据,交给Model去执行业务方法,执行的过程中需要的反馈以及结果,让View进行对应的显示。
好了,完整的代码结构讲完了,大家可以在https://github.com/GdinKing/MVPDemo下载源码。
希望大家能对MVP有更深的认识。