Android开发MVP架构三分钟上手应用

写在前面#

最近看到了好多朋友写的关于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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,362评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,330评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,247评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,560评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,580评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,569评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,929评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,587评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,840评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,596评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,678评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,366评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,945评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,929评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,165评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,271评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,403评论 2 342

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,393评论 25 707
  • 前言 MVC、MVP、MVVM一直以来都是Android应用常见的架构模式,都是为了抽离出UI逻辑和业务逻辑。但是...
    希格斯子阅读 1,173评论 0 1
  • 项目启发来自谷歌的同类框架项目 android-architecture利用一个相同的项目,使用不同的框架实现之...
    boredream阅读 4,198评论 9 67
  • 博大精深的汉字,每一个都奇妙无穷。虽然我一直对汉字心存敬畏,但偶尔玩笑一下,故意曲解一个博学生一乐,也是有的。今天...
    枫儿a阅读 788评论 0 17
  • 每个老人都打着爱孩子的角度,可是我却看不见他们的真爱!一个嗷嗷待哺的婴儿,在需要妈妈的怀抱时,他们为了满足自己的私...
    莫莫077阅读 195评论 0 0