写在前面#
最近看到了好多朋友写的关于MVP架构详解,浅谈...对于看文章三分钟热度的我都没有看完...趁着周末有时间,写了个demo,针对MVP进行菜鸟级的解析(目的是简单了解和快速应用),下面聚精会神三分钟,看看你能不能有所收获.
一.MVC和MVP##
- 这部分是必须要了解的,我这里也是使用了网上比较好的总结.
1.MVC##
MVC的全称为Model-View-Controller,即模型-视图-控制器,提出MVC目的为了分离界面和业务逻辑。
- Model:处理数据和业务逻辑等
- View:显示界面,展示结果等
- Controller:控制流程,处理交互
android应用程序中mvc
- View : layout目录下的xml布局文件,设计应用的ui界面。展现数据
- Controller : Activity作为控制器,处理用户请求
- Model : 一般是一个javaBean对象
举个栗子:MVC模式就像是书店里工作模式.
- Model:可以理解为仓库管理员.他的工作是联系出版社,进货..
- View:书架,所有的数都展示在书架上
- Controller:老板,老板说这次我们需要买一批网络小说,这些书就出现在了书架上.这里面就涉及到M-V-C模式的.
2.MVP##
MVP的全称为Model-View-Presenter,即模型-视图-协调器(主持者)
- Model:处理数据和业务逻辑等
- View:显示界面,展示结果等
- Presenter:协调Model和View模块工作,处理交互
关于优缺点等更深入的了解大家也可以看简书上相关文章,写得很仔细,我这里重点就是使用,所以就不做过多讲解.
二.案例分析##
针对MVP架构,查阅相关的文章和使用后我写了一个Demo,这个demo是为说明使用而量身设计的,有一定的代表性,源码在github上有.
1.效果图##
![Uploading GIF_776518.gif . . .]
![Uploading 2017-02-19_233539_830888.png . . .]
2.结构分析##
通过结构分析在脑海中有了一个基本框架,接下来就是去实现这个框架.
- 共同点
- 每个界面都是RecyclerView,有下拉刷新和上拉加载
- 不同:
- 每个RecyclerView的item内容不同,也就是对应的adapter不同
3.上代码##
3.1 代码结构###
参考我的工程结构
1.抽取基类:抽取基类的好处就是找到共性内容少写代码,代码简洁清爽
-
创建BaseFragment,作为fragment的基类
-
共性:创建view视图,初始化操作
public abstract class BaseFragment extends Fragment { public Context mContext; @Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { mContext = getActivity(); View view = initView(); //绑定view ButterKnife.bind(this, view); //初始化 init(); return view; } public void init(){}; /** * 实现的子类必须去实现的抽象方法,创建view * @return */ public abstract View initView();
}
-
-
定义View接口
定义抽象方法: 数据加载成功
-
View和Presenter是一对一或这一对多的,并且Presenter和View是通过接口交互的
public interface BaseView { //数据加载成功后的回调 void OnLoadDataSuccess(); }
-
创建HomePresenter,对应 是首页的presenter(指挥官,老板)
public interface HomePresenter { //初始化请求网络数据 void loadDataList(); //下拉刷新 void refresh(); //加载更多 void loadMoreData(); ArrayList<HomeDataBean> getDataList(); }
-
创建BaseListFragment,这里大家需要特别注意,那些方法是要去子类必须去执行的,整体的业务逻辑应该是什么样的
public abstract class BaseListFragment<T> extends BaseFragment implements BaseView { @BindView(R.id.base_recycle_view) RecyclerView mBaseRecycleView; @BindView(R.id.base_down_refresh) SwipeRefreshLayout mBaseDownRefresh; private BaseListPresenter mPresenter; private RecyclerView.Adapter mAdapter; @Override public void init() { //初始化presenter mPresenter = getPresenter(this); initRecycleView(); //加载数据 mPresenter.loadDataList(); //设置RecyclerView的滚动事件 mBaseRecycleView.addOnScrollListener(scrollListener); //设置下拉刷新进度框的颜色,参数可以设置多个颜色,转一圈换一种颜色 mBaseDownRefresh.setColorSchemeResources(R.color.colorPrimary); //监听下拉 mBaseDownRefresh.setOnRefreshListener(listener); } private void initRecycleView() { mAdapter = getAdapter(); mBaseRecycleView.setLayoutManager(new LinearLayoutManager(getContext()));//设置布局管理器 mBaseRecycleView.setAdapter(mAdapter); } @Override public View initView() { return View.inflate(mContext, R.layout.base_item_fragment, null); } //加载数据成功 @Override public void OnLoadDataSuccess() { mAdapter.notifyDataSetChanged(); //更新完成数据之后,隐藏进度圈 mBaseDownRefresh.setRefreshing(false); } private RecyclerView.OnScrollListener scrollListener = new RecyclerView.OnScrollListener() { @Override public void onScrollStateChanged(RecyclerView recyclerView, int newState) { super.onScrollStateChanged(recyclerView, newState); LinearLayoutManager manager = (LinearLayoutManager) recyclerView.getLayoutManager(); //得到总共item条目数量,最后一个可见条目 int totaleItme = manager.getItemCount(); int lastVisibleItem = manager.findLastCompletelyVisibleItemPosition(); if (newState == RecyclerView.SCROLL_STATE_IDLE) { //闲置状态 if (lastVisibleItem == totaleItme - 1) { //最后一条,第一条是0 //加载更多数据,开始位置是当前集合中的对象个数 mPresenter.loadMoreData(); } } } }; private SwipeRefreshLayout.OnRefreshListener listener = new SwipeRefreshLayout.OnRefreshListener() { @Override public void onRefresh() { //下拉刷新事件的监听 mPresenter.refresh(); } }; /** * 子类必须返回presenter对象 * * @return */ protected abstract BaseListPresenter getPresenter(BaseListFragment<T> tBaseListFragment); /** * 定义抽象方法,子类必须返回adapter对象 * * @return */ public abstract RecyclerView.Adapter getAdapter(); }
上面给大家扔了一堆代码,看起来很枯燥,但是如果不放,我直接说会更枯燥.由于我画图水平一般,就没有拿出漂亮的结构图,见谅.上面代码就是在对app功能分析的基础上,去分析业务逻辑和基本框架,抽取共同点完成的.这里我们完成了一小步
-
2.实现
-
完成HomeFragment,实现BaseListFragment必须要实现两个抽象方法
public class HomeFragment extends BaseListFragment { private HomePresenterImpl mHomePresenter; @Override protected BaseListPresenter getPresenter(BaseListFragment baseListFragment) { mHomePresenter = new HomePresenterImpl(baseListFragment); return mHomePresenter; } @Override public RecyclerView.Adapter getAdapter() { return new HomeRecycleViewAdapter(mContext, mHomePresenter.getDataList()); } }
-
创建HomePresenter的实现类,处理首页界面的业务逻辑
public class HomePresenterImpl implements BaseListPresenter<HomeDataBean> { private static final String TAG = "HomePresenterImpl"; private ArrayList<HomeDataBean> mDatas; private Handler mHandler; private HomeFragment mBaseListFragment; public HomePresenterImpl(BaseListFragment baseListFragment) { mBaseListFragment = (HomeFragment) baseListFragment; mDatas = new ArrayList<>(); mHandler = new Handler(); } @Override public void loadDataList() { loadData(0); } @Override public void refresh() { mDatas.clear(); loadData(0); } @Override public void loadMoreData() { loadData(mDatas.size()); } @Override public ArrayList<HomeDataBean> getDataList() { return mDatas; } /** * 请求网络数据 * * @param offSize 从什么位置开始下载,每次请求数据的长度是固定的10条 */ private void loadData(int offSize) { OkHttpClient httpClient = new OkHttpClient(); String url = URLProviderUtil.getHomeUrl(offSize, 10); Request request = new Request.Builder().get().url(url).build(); httpClient.newCall(request).enqueue(new Callback() { @Override public void onFailure(okhttp3.Call call, IOException e) { } @Override public void onResponse(okhttp3.Call call, Response response) throws IOException { //得到返回的json数据 String json = response.body().string(); Gson gson = new Gson(); //将json数据解析成包含对象的集合 ArrayList<HomeDataBean> list = gson.fromJson(json, new TypeToken<List<HomeDataBean>>() { }.getType()); //将从网络上请求到的数据添加到集合中 mDatas.addAll(list); //不能在子线程更新UI,通过handler添加 mHandler.post(new Runnable() { @Override public void run() { //数据加载完成-->通知更新数据..更新完成数据之后,隐藏进度圈 mBaseListFragment.OnLoadDataSuccess(); } }); } }); } }
通过上面的操作完成了首页界面.
-
3.2 结构分析###
完成首页界面后可能对这个MVP还是没有什么直接的感受,要我来说,主要还是有下面几点明显的优势的
-
降低耦合度,实现了Model和View真正的完全分离,可以修改View而不影响Modle
- 针对上面的代码项目我们发现view层的工作就是当一切工作顺利执行后去显示数据,刷新数据,显示进度圈,出现错误显示错误(这里没有做),view层没有去操作数据.
- Modle层的操作写在了Present中,就是加载数据,加载数据,加载完成告诉presenter,presenter会继续通知view执行操作.
-
模块职责划分明显,层次清晰
- 对应每个模块分工明确,你做完自己的工作就好了
利于测试驱动开发
还有等等..好处
缺点
- Presenter中除了应用逻辑以外,还有大量的View->Model,Model->View的手动同步逻辑,造成Presenter比较笨重,维护起来会比较困难。
- 代码复杂度大
- 还有等等..
三.小结##
本以为理解简单写起了就容易了,没想到还是这么难,具体还是要大家去体会了,多用才能熟能生巧.我的源码在GitHub上有.大家可以看看.以后有好多内容还是会继续分享,第一也是为了提升自己,另一方面我们也可以共同进步.有问题还希望多多指导.我的仓库:https://github.com/hh-pan/Player.git