浅谈Android中的MVP架构

一、前言

Android中的Mvp模式也不是什么新概念了,网上也有许关于Mvp的案例,但是感觉很多写得还是不够清晰,并且大多实际的项目涉及到数据绑定以及响应式编程的东西,给许多刚想了解Mvp架构的朋友带来了不必要的理解难度。Android也是丢了很长一段时间,平日里逛这些社区时,这些关于架构的概念还是接触了不少,但是“纸上识来终觉浅,要知此事需躬行”,所以这里我写篇博客总结一下。

二、介绍

MVP作为现在android最主流的架构,还是在很多方面有着优势的。在传统的模式中,本该为视图层的却是非常臃肿的,在activity或者fragment中,既做了数据的绑定,又进行了网络的请求和Ui的更新。而在MVP模式中就对这一点做了很好的解耦,这里也还是拿一张图来看看
图片描述

很显然MVP,也就是分为三层,其中View层只负责视图的显示以及更新,Model只负责数据的获取(如网络请求等),而Presenter层则是负责View和Model通信的桥梁,接收model请求的结果后返回给View,或者接收View层的信息后到Model层进行请求,以及其它的一些逻辑,这样一来Presenter层就是纯java代码。

优点

这样一来,就很好的体现了单一职责的原则,并且严格分为三层,即使后期业务变多,结构仍然清晰,非常利于项目后期的维护,当有新的需求时,只需将相关的需求写在接口,然后实现这个接口,无需顾及之前的结构,免去几方面去调整的头痛。另外,还能更好的对接口代码进行单元测试。

缺点以及解决的一些办法

使用Mvp模式去构建项目,会造成类文件和接口文件的过多,进而增大包的体积。这里的话,可以采用官方的做法,写一个Contract接口,然后把与Mvp三层的相关接口全部列入到里面去,类似下面这种。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public interface Contract {
public interface IModel {
xxx;
xxx;
}
public interface IPresenter {
xxx;
xxx;
}
public interface IView {
xxx;
xxx;
}
}

除此之外,就是内存泄漏的问题。用户关闭了View层,但这时Model层如果仍然在进行耗时操作,因为Presenter层也持有View层的引用,所以造成垃圾回收器无法对View层进行回收,这样一来,就造成了内存泄漏。这里可以重写onDestroy()方法,在View销毁时强制回收掉Presenter;还有一个解决办法就是采用弱引用的方式,如下

1
2
3
WeakReference<xxx> refrence = new WeakRefrence<>(this);
// 使用时直接 就能获得对象的引用
reference.get();

然后在引用进行引用之前,都需要判断引用不为空,以防止空指针异常。

三、实际案例

这里使用XRecyclerView进行上拉加载和下拉刷新,使用AsyncTask进行异步请求。这里就还是分开了来写
首先是Model接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public interface ICommonModel {
/**
* 传入List和当前页数
* @param
* @param pageIndex
*/
void getData(int pageIndex,RequestResult result);
/**
* 记录数量
* @return
*/
int count();
/**
* 请求接口的回调
* @param <T>
*/
public interface RequestResult<T> {
void onStart();
void onSuccess(List<T> beanList);
void onFailure();
void onEnd();
}
}

model的实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
public class HomeCircleModelImpl implements ICommonModel {
private static int count = 0;
private JSONArray results;
private ICommonModel.RequestResult<HomeCircleBean> result;
private List<HomeCircleBean> circleBeanList;
@Override
public void getData(int pageIndex, RequestResult result) {
this.result = result;
new HomeCircleAsyncTask().execute(ApiUtils.CIRCLES+"?page="+pageIndex);
}
@Override
public int count() {
return count;
}
/**
* 异步加载Json数据
*/
class HomeCircleAsyncTask extends AsyncTask<String, Void, List<HomeCircleBean>> {
@Override
protected void onPreExecute() {
super.onPreExecute();
result.onStart();
}
@Override
protected List<HomeCircleBean> doInBackground(String... strings) {
IRequest request = new BaseRequest(strings[0]);
IHttpClient mHttpClient = new OkHttpClientImpl();
IResponse response = mHttpClient.get(request);
String data = response.getData().toString();
try {
JSONObject jsonObject = new JSONObject(data);
count = jsonObject.getInt("count");
results = jsonObject.getJSONArray("results");
} catch (JSONException e) {
e.printStackTrace();
}
Gson gson = new Gson();
circleBeanList =
gson.fromJson(results.toString(), new TypeToken<List<HomeCircleBean>>() {
}.getType());
return circleBeanList;
}
@Override
protected void onPostExecute(List<HomeCircleBean> homeCircleBeans) {
super.onPostExecute(homeCircleBeans);
if (homeCircleBeans != null) {
result.onSuccess(homeCircleBeans);
} else {
result.onFailure();
}
result.onEnd();
}
}
}

然后是Presenter接口

1
2
3
4
5
6
7
8
9
10
11
12
public interface IHomeCirclePresenter {
/**
* 刷新
*/
void onRefresh();
/**
* 加载更多
*/
void onLoadMore();
}

Presenter实现类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
public class HomeCirclePresenter implements ICommonPresenter {
public static CircleListAdapter adapter;
private ICommonModel circleModel;
private ICommonView circleView;
private int mCurrentPageIndex = 1;
private List<HomeCircleBean> circleBeanLists;
public HomeCirclePresenter(ICommonModel circleModel, ICommonView circleView) {
this.circleModel = circleModel;
this.circleView = circleView;
this.circleBeanLists = new ArrayList<>();
adapter = new CircleListAdapter(circleBeanLists);
}
@Override
public void onRefresh() {
circleBeanLists.clear();
mCurrentPageIndex = 1;
circleModel.getData(mCurrentPageIndex, new ICommonModel.RequestResult() {
@Override
public void onStart() {
circleView.showLoading();
}
@Override
public void onSuccess(List beanList) {
circleBeanLists.addAll(beanList);
}
@Override
public void onFailure() {
}
@Override
public void onEnd() {
adapter.notifyDataSetChanged();
circleView.hideLoading();
}
});
}
@Override
public void onLoadMore() {
mCurrentPageIndex++;
circleModel.getData(mCurrentPageIndex, new ICommonModel.RequestResult() {
@Override
public void onStart() {
circleView.showLoading();
}
@Override
public void onSuccess(List beanList) {
if (adapter.getItemCount() >= circleModel.count()) {
circleView.noMoreData();
} else {
circleBeanLists.addAll(beanList);
}
}
@Override
public void onFailure() {
}
@Override
public void onEnd() {
adapter.notifyDataSetChanged();
circleView.hideLoading();
}
});
}
}

最后是View接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public interface ICommonView {
/**
* 显示进度
*/
void showLoading();
/**
* 隐藏进度
*/
void hideLoading();
/**
* 数据加载完成
*/
void noMoreData();
}

实现类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
public class HomeCircleView extends BaseFragment implements ICommonView {
private Dialog dialog;
private XRecyclerView mRecyclerView;
private View mView;
private ICommonPresenter presenter;
private ICommonModel model;
@Override
public int setResourceLayoutId() {
return R.layout.all_circle_layout;
}
@Override
public int setRecyclerViewId() {
return R.id.mRecyclerView;
}
@Override
public void initView() {
mView = getmView();
model = new HomeCircleModelImpl();
WeakReference<HomeCircleView> reference = new WeakReference<>(this);
presenter = new HomeCirclePresenter(model,reference.get());
}
@Override
public void initListener() {
mRecyclerView = getmRecyclerView();
mRecyclerView.addItemDecoration(new XRecyclerViewDivider(mView.getContext(), LinearLayoutManager.HORIZONTAL));
mRecyclerView.setAdapter(adapter);
mRecyclerView.setLoadingListener(new XRecyclerView.LoadingListener() {
@Override
public void onRefresh() {
refreshData();
}
@Override
public void onLoadMore() {
loadMoreData();
}
});
}
@Override
public void refreshData() {
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
presenter.onRefresh();
mRecyclerView.refreshComplete();
}
},500);
}
private void loadMoreData() {
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
presenter.onLoadMore();
mRecyclerView.loadMoreComplete();
}
},500);
}
@Override
public void showLoading() {
dialog = ToastUtils.createLoadingDialog(mView.getContext(),"正在努力...加载中~");
dialog.show();
}
@Override
public void hideLoading() {
dialog.dismiss();
}
@Override
public void noMoreData() {
mRecyclerView.setNoMore(true);
}
}

然后看看传统模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
public class HomeCircleFragment extends BaseFragment {
public static final String SEARCH_CIRCLE_RESULTS = "search_circle_result";
private View mView;
private XRecyclerView mRecyclerView;
private CircleListAdapter adapter;
private List<HomeCircleBean> circleList = new ArrayList<>();
private HomeCircleBean circleBean;
private int mCurrentPageIndex = 1;
private HomeCircleAsyncTask asyncTask;
private JSONArray result;
private List<HomeCircleBean> circleBeanList;
private int count;
private Dialog dialog;
@Override
public int setResourceLayoutId() {
return R.layout.all_circle_layout;
}
@Override
public int setRecyclerViewId() {
return R.id.mRecyclerView;
}
@Override
public void initView() {
mView = getmView();
dialog = ToastUtils.createLoadingDialog(mView.getContext(),getString(R.string.common_loading));
adapter = new CircleListAdapter(circleList);
}
@Override
public void initListener() {
mRecyclerView = getmRecyclerView();
mRecyclerView.addItemDecoration(new XRecyclerViewDivider(mView.getContext(), LinearLayoutManager.HORIZONTAL));
mRecyclerView.setAdapter(adapter);
mRecyclerView.setLoadingListener(new XRecyclerView.LoadingListener() {
@Override
public void onRefresh() {
refreshData();
}
@Override
public void onLoadMore() {
loadmoreData();
}
});
}
/**
* 刷新数据
*/
@Override
public void refreshData() {
circleList.clear();
mCurrentPageIndex = 1;
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
new HomeCircleAsyncTask().execute(ApiUtils.CIRCLES+"?page="+mCurrentPageIndex);
mRecyclerView.refreshComplete();
}
},500);
}
private void loadmoreData() {
mCurrentPageIndex++;
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
if (adapter.getItemCount() >= count) {
mRecyclerView.setNoMore(true);
} else {
new HomeCircleAsyncTask().execute(ApiUtils.CIRCLES+"?page="+mCurrentPageIndex);
mRecyclerView.loadMoreComplete();
}
}
},500);
}
/**
* 异步加载Json数据
*/
class HomeCircleAsyncTask extends AsyncTask<String, Void, List<HomeCircleBean>> {
@Override
protected void onPreExecute() {
super.onPreExecute();
dialog.show();
}
@Override
protected List<HomeCircleBean> doInBackground(String... strings) {
IRequest request = new BaseRequest(strings[0]);
IHttpClient mHttpClient = new OkHttpClientImpl();
IResponse response = mHttpClient.get(request);
String data = response.getData().toString();
try {
JSONObject jsonObject = new JSONObject(data);
count = jsonObject.getInt("count");
result = jsonObject.getJSONArray("results");
} catch (JSONException e) {
e.printStackTrace();
}
Gson gson = new Gson();
circleBeanList =
gson.fromJson(result.toString(), new TypeToken<List<HomeCircleBean>>() {
}.getType());
return circleBeanList;
}
@Override
protected void onPostExecute(List<HomeCircleBean> homeCircleBeans) {
super.onPostExecute(homeCircleBeans);
for (HomeCircleBean circle : homeCircleBeans) {
circleBean =
new HomeCircleBean(circle.getId(), circle.getImage(), circle.getName(), circle.getDesc(),
circle.getUser(), circle.getAddress(), circle.getAdd_time());
circleList.add(circleBean);
}
adapter.notifyDataSetChanged();
dialog.dismiss();
}
}
}

可以比较一下这二者的区别,虽然现在看来传统模式下代码更加少,但是整体比较乱,视图,数据,请求之间的耦合度高。
因为这些代码是直接从项目抽出来的,还封装了一些东西,这里就不列出来了,后面会附上github地址,关键是具体实现需求时,对整体架构思路的清晰。

四、总结

因为有一段时间没搞android了,在实现了需求后,本想不去管了然后去学习新的东西,但是也意识到了学再多新的东西,用不到的情况下照样会忘记,来来去去实力并没有得到提升,学而不思则罔 思而不学则殆,多增加在原有基础上的优化,以增强自己的内功,所以决定把传统模式的项目用MVP重构,这里我保留了传统模式和MVP模式下的代码,以方便两者的比较学习。
总的来说,MVP使项目整体结构更加清晰,高内聚、低耦合。
那这篇博客就先到这里了。

×

纯属好玩

扫码支持
扫码打赏,你说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

文章目录
  1. 1. 一、前言
  2. 2. 二、介绍
    1. 2.1. 优点
    2. 2.2. 缺点以及解决的一些办法
  3. 3. 三、实际案例
  4. 4. 四、总结
,