最近刚刚接触MVP模式,试着做一下笔记。
关于MVP模式
网上关于MVP模式的资料已经很多了,看了几篇介绍的,个人觉得Android MVP模式 简单易懂的介绍方式这篇讲得最通俗易懂了,而且也考虑到了很多方面。这里就不再赘述了。
MVP demo
网上大多数的博客给的demo都挺简单的,所以我想写一个稍微复杂点的demo,主要用到了Fragment和RecyclerView。主要是参考了github上的RecyclerView-Examples,不过他写得比较深一点,还用到了抽屉栏和一些其它的技术,所以我把RecyclerView这部分抽了出来,比较方便初学者的学习。
先上效果图:
然后是代码目录结构:
从这张图就可以很明显的感觉到了MVP的缺点了——代码量一下子变得好多。写这个demo的时候确实也花费了很多时间,不过写完后细想一下,觉得这是值得的,因为结构一下子变得很清晰,想定位某些问题的话很快便能找到对应的代码。也不至于说几天过后再回过头来看觉得这不是你的代码了。
大致思路
在PictureFragment的onResume
方法中去模拟加载图片操作,由于这里是View和Model的交互,所以理所应当的交给PicturePresenter去处理。在PicturePresenterImpl中是通过PictureInteractor去和Model进行交互的。取得数据后,返回给LoaderListener的onFinish
方法去处理,在该方法中又调用了PictureView的showPictures
方法,最终完成了图片的加载和显示。
看起来会有点绕,不过当你习惯了之后便会觉得思路还是很清晰的,即当View想和Model交互时,交给Presenter去处理,Presenter通过Interactor取得Model中的数据后,又交给了View去进行显示。
PictureView
目前我写MVP的是写好布局文件后,先大概想一下该页面需要做什么UI操作,如进度条显示,Toast通知等,然后把这些已经确定的操作写进PictureView接口中,开发过程中如果还发现需要补充再添加进去就OK了。
public interface PictureView {
void showProgressBar();
void hideProgressBar();
void showMsg(String msg);
void showPictures(List<Picture> pictures);
}
没错,PictureView就是这么简单而已。
PictureFragment
首先需要说明的是,在写到这一步的时候,代码中和Presenter有关的代码都是还没添加进去的,请自动过滤掉先~
在PictureFragment中实现PictureView接口,在这一步中你完全可以假设你想要的数据已经取到了,可以尽情的做你想做的事了(尽管我们还没写Presenter)。看到没有,这里MVP的优势已经显现出来了。每一层都有自己的职责,你只需要管好自己的就行了。
public class PictureFragment extends Fragment implements PictureView{
private RecyclerView mRecyclerView;
private ProgressBar mProgress;
private PictureAdapter mAdapter;
private PicturePresenter mPresenter;
public static PictureFragment newInstance() {
return new PictureFragment();
}
public PictureFragment() {
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_picture, container, false);
initView(view);
mPresenter = new PicturePresenterImpl(this);
return view;
}
@Override
public void onResume() {
super.onResume();
mPresenter.onResume();
}
@Override
public void onDestroy() {
super.onDestroy();
mPresenter.onDestroy();
}
private void initView(View view) {
mRecyclerView = (RecyclerView) view.findViewById(R.id.recycler_view);
mProgress = (ProgressBar) view.findViewById(R.id.progress_bar);
setupRecyclerView();
}
private void setupRecyclerView() {
mRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
}
@Override
public void showProgressBar() {
mProgress.setVisibility(View.VISIBLE);
mRecyclerView.setVisibility(View.INVISIBLE);
}
@Override
public void hideProgressBar() {
mProgress.setVisibility(View.INVISIBLE);
mRecyclerView.setVisibility(View.VISIBLE);
}
@Override
public void showMsg(String msg) {
Toast.makeText(getActivity(), msg, Toast.LENGTH_LONG).show();
}
@Override
public void showPictures(List<Picture> pictures) {
mAdapter = new PictureAdapter(pictures);
mAdapter.setRecyclerItemClickListener(new OnRecyclerItemClickListener() {
@Override
public void onItemClick(int pos) {
mPresenter.onItemClick(pos);
}
});
mRecyclerView.setAdapter(mAdapter);
}
}
写完Fragment之后,大概也知道自己需要在哪些地方做业务逻辑的操作了,接下来就可以写Presenter了。
PicturePresenter
在Presenter这部分是MVP中最关键且最难写的一部分了,所以在写这一步的时候请尽量保持头脑思路清晰。
public interface PicturePresenter {
void onResume();
void onDestroy();
void onItemClick(int pos);
}
PicturePresenterImpl
在构造方法中,用PictureView初始化自己的成员变量,然后在onDestroy
中设为NULL
,防止内存泄露。
在与Model的交互中需要用到PictureInteractor,当然你也可以直接去取数据,不过我不觉得这是一种好的思路。所以把要交给View去操作的事先给办了,然后再来写PictureInteractor,再回过头来把未完成的事情做好。
public class PicturePresenterImpl implements PicturePresenter, LoaderListener {
private PictureView mPictureView;
private PictureInteractor mInteractor;
public PicturePresenterImpl(PictureView pictureView) {
this.mPictureView = pictureView;
mInteractor = new PictureInteractorImpl();
}
@Override
public void onResume() {
mPictureView.showProgressBar();
mInteractor.loadPictures(this);
}
@Override
public void onDestroy() {
mPictureView = null;
}
@Override
public void onItemClick(int pos) {
mPictureView.showMsg(String.valueOf(pos));
}
@Override
public void onFinish(List<Picture> pictures) {
mPictureView.hideProgressBar();
mPictureView.showPictures(pictures);
}
}
PictureInteractor
public interface PictureInteractor {
void loadPictures(LoaderListener listener);
}
PictureInteractorImpl
@Override
public void loadPictures(final LoaderListener listener) {
// 用Handler延时模拟下载操作
new Handler(Looper.getMainLooper())
.postDelayed(new Runnable() {
@Override
public void run() {
listener.onFinish(createPictures());
}
}, 2000);
}
private List<Picture> createPictures() {
ArrayList<Picture> pictures = new ArrayList<>();
for (int i = 0; i < pictureNames.length; i++) {
pictures.add(new Picture(pictureNames[i], pictureImages[i]));
}
return pictures;
}
其它
关于其它部分,就不继续写下去了,不是MVP的重点,具体的请看源码
总结
在还没下笔开始写代码的时候,觉得MVP模式很难理解,甚至会觉得代码跳来跳去的很乱。但认真写下来之后却发现收获很多。
还有一点是不知道从哪开始入手,因为MVP把View和Model分开了,这两层就变成独立的了,突然间就不知道该从哪写入手了。所以,以上是我自己的一点经验总结吧,先把Model定下来,这是最容易的,毕竟你总得知道你写的页面需要什么数据吧,然后写View,在这个过程中确定自己需要做哪些业务逻辑的操作,接着写Presenter,接着再写Interactor。中间遇到一些暂时写不了的就先放着,等你写完其它的之后很容易就能定位到相应的代码的。
也许等我对MVP更熟悉了之后会有更好的思路。
最后,如果你看懂了这个Demo,建议看看我参考的RecyclerView-Examples,在这里你能进一步地理解MVP模式。