应用开发总会碰到 UI、数据、业务逻辑。逻辑一复杂就要分框架。最早是 MVC,后来也用在 Android 上。MVP 可以看作 MVC 的升级:把 Activity 从「又当 View 又当 Controller」里拆出来。
一、概述:MVC 为什么会胖
MVC 架构
M : 业务层和模型层,相当与javabean和我们的业务请求代码
model层主要负责:
· 从网络,数据库,文件,传感器,第三方等数据源读写数据。
· 对外部的数据类型进行解析转换为APP内部数据交由上层处理。
· 对数据的临时存储,管理,协调上层数据请求。
V : 视图层,对应Android的layout.xml布局文件
C : 控制层,对应于Activity中对于UI 的各种操作
纸面很清晰,实际开发里请求代码经常塞进 Activity;layout.xml 只能给默认 UI,视图变化也被丢进 Activity;Activity 本身还承担控制层。于是 Activity 集齐 MVC,从几百行涨到几千行,维护成本越来越高。
新的 MVP 架构
M : 还是业务层和模型层
V : 视图层的责任由Activity来担当
P : 新成员Prensenter 用来代理 C(control) 控制层,是View和Model的中间“桥梁”,负责View和Model间的交互
MVP 与 MVC 最大的不同,是 Activity 的职责:从 C(控制层)变成 V(视图层),只管怎么显示。控制交给 Presenter。这样解开 Activity 对控制层和视图层的过度耦合。

| 角色 |
MVC 里谁扛 |
MVP 里谁扛 |
| Model |
Bean + 请求(常被塞进 Activity) |
仍负责数据源、解析、暂存 |
| View |
layout.xml,变化却写在 Activity |
Activity / Fragment 只负责显示 |
| 控制 |
Activity 既是 C 又掺 V |
Presenter 当 View 与 Model 的桥 |
二、实例
用 MVP 实现下图效果:

第一步 View 层
先定义 UI 能做什么,Fragment 只实现接口、把点击交给 Presenter:
/**
* 定义UI视图逻辑
*/
public interface ICheapView {
void setListViewAdapter(ReadNewsAdapter adpater);
void showToastMessage(String message);
void setBannerLayoutData(List<AdsBean> list);
}
public class CheapFragment extends BaseMvpFragment<ICheapView, CheapPresenter> implements ICheapView, View.OnClickListener {
private EditText mSearchKey;
private ListView mListView;
private FrameLayout mListHeadView;
private BannerLayout mBannerLayout;
public static CheapFragment newInstance() {
CheapFragment fragment = new CheapFragment();
return fragment;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
mListHeadView = (FrameLayout) inflater.inflate(fragment_cheap_banner_layout, null);
return inflater.inflate(R.layout.fragment_cheap_layout, container, false);
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
mSearchKey = (EditText) getView().findViewById(R.id.com_search_edit);
mBannerLayout = (BannerLayout) mListHeadView.findViewById(R.id.banner);
mListView = (ListView) getView().findViewById(R.id.cheap_listview);
mListView.addHeaderView(mListHeadView);
mSearchKey.setOnClickListener(this);
mPresenter.setListViewAdapter();
mPresenter.setListBannerViewData();
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.com_search_edit:
getActivity().onSearchRequested();
break;
}
}
@Override
public CheapPresenter initPresenter() {
return new CheapPresenter(getActivity());
}
@Override
public void setListViewAdapter(ReadNewsAdapter adpater) {
mListView.setAdapter(adpater);
}
@Override
public void showToastMessage(String message) {
Toast.makeText(getActivity(), message, Toast.LENGTH_SHORT).show();
}
@Override
public void setBannerLayoutData(List<AdsBean> urls) {
List<String> list = new ArrayList<>();
for (AdsBean ads : urls) {
list.add(ads.getImgsrc());
}
mBannerLayout.setViewUrls(list);
//添加监听事件
mBannerLayout.setOnBannerItemClickListener(new BannerLayout.OnBannerItemClickListener() {
@Override
public void onItemClick(int position) {
showToastMessage(String.valueOf(position));
}
});
}
}
第二步 Presenter 层
业务接口由 Presenter 实现:向 Model 要数据,成功后再调 View。
/**
* 定义业务逻辑
*/
public interface ICheapPresenter {
void setListViewAdapter();
void setListBannerViewData();
}
public class CheapPresenter extends BasePresenter<ICheapView> implements ICheapPresenter{
private ICheapModel mCheapModel;
private ReadNewsAdapter mReadNewsAdapter;
public CheapPresenter(Context context) {
mCheapModel = new CheapModel();
mReadNewsAdapter = new ReadNewsAdapter(context);
}
@Override
public void setListViewAdapter() {
mView.setListViewAdapter(mReadNewsAdapter);
}
@Override
public void setListBannerViewData() {
mCheapModel.setListAndAdsData(new CallbackListener<NewsTextBean>() {
@Override
public void onSuccess(NewsTextBean data) {
List<NewsTextItemBean> newsTextItemBeanList = data.getT1348647909107();
mReadNewsAdapter.addDatas(0,newsTextItemBeanList);
List<AdsBean> adsLists = newsTextItemBeanList.get(0).getAds();
mView.setBannerLayoutData(adsLists);
}
@Override
public void onFailure(String message) {
mView.showToastMessage(message);
}
});
}
}
第三步 Model 层
网络请求放在 Model,这里用 Retrofit 拉网易新闻接口:
public interface ICheapModel {
void setListAndAdsData(final CallbackListener<NewsTextBean> listener);
}
public class CheapModel implements ICheapModel {
@Override
public void setListAndAdsData(final CallbackListener<NewsTextBean> listener) {
Retrofit retrofit = new Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create())
.baseUrl("http://c.m.163.com/")
.build();
RetrofitCall call = retrofit.create(RetrofitCall.class);
Call<NewsTextBean> mCall = call.getNewsText();
mCall.enqueue(new Callback<NewsTextBean>() {
@Override
public void onResponse(Call<NewsTextBean> call, Response<NewsTextBean> response) {
NewsTextBean newsTextBean = response.body();
listener.onSuccess(newsTextBean);
}
@Override
public void onFailure(Call<NewsTextBean> call, Throwable t) {
listener.onFailure(t.getMessage());
}
});
}
}
三、基类:绑定与解开 View
BaseMvpFragment 负责实例化 Presenter;BasePresenter 持有 View 引用,销毁时清掉,避免泄漏。
public abstract class BasePresenter<T> {
/*这个基类的Presenter 主要的作用就是将当前Presenter持有的view 在合适的时候给清除掉*/
public T mView;
public void attach(T mView) {
this.mView = mView;
}
public void dettach() {
mView = null;
}
}
public abstract class BaseMvpFragment<V, T extends BasePresenter<V>> extends Fragment {
/*定义一个Presenter 用于解绑持有的View
*
* 在onAttach进行初始化Presenter的操作
* 在onCreate中进行绑定
* 在onDestroy 中进行解绑
*
**/
public T mPresenter;
@Override
public void onAttach(Context context) {
super.onAttach(context);
mPresenter = initPresenter();
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mPresenter.attach((V) this);
}
@Override
public void onDestroy() {
super.onDestroy();
mPresenter.dettach();
}
/* 这里是适配为不同的View 装载不同Presenter*/
public abstract T initPresenter();
}
四、参考学习
一句话总结:MVP 让 Activity/Fragment 只做 View,Presenter 当桥,Model 管数据和请求;基类里 attach/detach,页面销毁时把 View 引用清掉。