MVP项目练习

写在前面

最近看到了好多朋友写的关于MVP架构详解,浅谈…对于看文章三分钟热度的我都没有看完…趁着周末有时间,写了个demo,针对MVP进行菜鸟级的解析(目的是简单了解和快速应用),下面聚精会神三分钟,看看你能不能有所收获.

一.MVC和MVP

这部分是必须要了解的,我这里也是使用了网上比较好的总结.

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模式的.

MVP

MVP的全称为Model-View-Presenter,即模型-视图-协调器(主持者)
这里写图片描述

  • Model:处理数据和业务逻辑等
  • View:显示界面,展示结果等
  • Presenter:协调Model和View模块工作,处理交互

关于优缺点等更深入的了解大家也可以看简书上相关文章,写得很仔细,我这里重点就是使用,所以就不做过多讲解.

  • MVP的使用步骤:

1.创建 IPresenter 接口,把所有业务逻辑的接口都放在这里,并创建它的实现 PresenterCompl(在这里可以方便地查看业务功能,由于接口可以有多种实现所以也方便写单元测试)。

2.创建 IView 接口,把所有视图逻辑的接口都放在这里,其实现类是当前的 Activity/Fragment。

3.Activity 里包含了一个 IPresenter,而 PresenterCompl 里又包含了一个 IView 并且依赖了 Model。Activity 里只保留对 IPresenter 的调用,其它工作全部留到 PresenterCompl 中实现。

4.Model 并不是必须有的,但是一定会有 View 和 Presenter

案例分析

针对MVP架构,查阅相关的文章和使用后我写了一个Demo,这个demo是为说明使用而量身设计的,有一定的代表性,源码在github上有.

  • 效果图
    这里写图片描述
    这里写图片描述
    这里写图片描述

结构分析

通过结构分析在脑海中有了一个基本框架,接下来就是去实现这个框架.

  • 共同点: 每个界面都是RecyclerView,有下拉刷新和上拉加载
  • 不同:每个RecyclerView的item内容不同,也就是对应的adapter不同

代码结构

这里写图片描述

参考我的工程结构

这里写图片描述

抽取基类

  • 创建BaseFragment,作为fragment的基类
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接口
    1. 定义抽象方法: 数据加载成功
    2. 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功能分析的基础上,去分析业务逻辑和基本框架,抽取共同点完成的.这里我们完成了一小步

实现

完成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();
			                    }
			                });
			            }
			        });
			    }
			}

通过上面的操作完成了首页界面.

结构分析

完成首页界面后可能对这个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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值