美文网首页AndroidCodingAndroid知识Android开发
MVP在Android中简易易懂的实现案例

MVP在Android中简易易懂的实现案例

作者: Swy2w | 来源:发表于2017-03-08 18:47 被阅读218次

    前言

    最进想做了一个新的小项目,总想来点对自己来说新鲜的东西。后面看到Google老大推荐使用MVP架构,顿时觉得不试试看都不好说是羡慕它的小弟了。良心大哥也专门在Github推出了一个项目Android Architecture Blueprints,用来展示Android用各种的MVP框架,也能算了官网教程了。在网络收集资料看了一下大神们的分析后,自己动手试了试。那么就马上上手来看一看MVP究竟如何。(本文都是围绕项目中todo-mvp部分来介绍

    MVP模式

    先上一张图,然后根据实际的代码我们来讲讲Google是怎么看待MVP架构的


    基类

    首先丁一两个Base接口,分别是作为Presenter和View的基类

    public interface BaseView<T> {
        // 为View设置Presenter
        void setPresenter(T presenter);
        // 初始化界面控件
        void initView(View view);
    }
    public interface BasePresenter {
        // 获取数据并改变界面显示,在todo-mvp的项目中的调用时机为Fragment的OnResume()方法中
        void start();
    }
    

    契约类

    官方事例中加如契约类来统一管理View和Presenter。这样整个功能可以在契约类一目了然。老大就是老大,这一点还是很厉害的。事例如下:

    public interface YourContract {
        interface View extends BaseView<Presenter>{
            //这里加View功能方法
            void showError();
            void showLoading();
            void Stoploading();
    
        }
        interface Presenter extends BasePresenter{
            // 同上
            void loatPosts(int PagerNum,boolean cleaing);
            void  reflush();
            void loadMore(int PagerNum);
        }
    }
    

    对了,其中BaseView中含方法setPresenter,该方法作用是在将presenter实例传入view中,其调用时机是presenter实现类的构造函数中。如下

    public YourPresenter(Context context, YourContract.View view) {
            this.context=context;
            this.view=view;
            this.view.setPresenter(this);
        }
    

    Activity的作用

    在讲这个之前,不知道大家有没有注意到上没图中,Fragment是作为View层而Activity是作为Presenter的,有没有想过Google为什么要推荐这样做呢?

    MVC中Activity的作用

    按照我们之前的习惯或者说在MVC模式中,Activity是作为View层和用户打交道,接收用户数据的输入和输出的。特别是我们会在Activity的声明周期中写入一些逻辑来实现我们想要的效果。这样很方便,但是后果是我们的Activity特别的臃肿,想一想如果我们一些通用的功能每个Activity里都要写一次不是一件很痛苦的事情。
    这个时候,Activity 不仅承担了 View 的角色,还承担了一部分的 Controller 角色,这样一来 V 和 C 就耦合在一起了,虽然这样写方便,但是如果业务调整的话,要维护起来就难了,而且在一个臃肿的 Activity 类查找业务逻辑的代码也会非常蛋疼,所以看起来有必要在 Activity 中,把 View 和 Controller 抽离开来,而这就是 MVP 模式的工作了。

    MVP中View层的实现

    至于为什么要选择Fragment作为View层的实现类。我看到网上有这两种说法,第一个原因是我们把activity作为一个全局控制类来创建对象,把fragment作为view,这样两者就能各司其职。第二个原因是因为fragment比较灵活,能够方便的处理界面适配的问题

    MVP 把 Activity 中的 UI 逻辑抽象成 View 接口,把业务逻辑抽象成 Presenter 接口,Model 类还是原来的 Model。

    来看一段代码就能知道Activity的作用了(毕竟代码能够一目了然):

         @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            initView();
            if (savedInstanceState!=null){
                mainFragment= (MainFragment) getSupportFragmentManager().getFragment(savedInstanceState,"MainFragment");
                bookmarksfragment=(BookmarksFragment) getSupportFragmentManager().getFragment(savedInstanceState,"BookmarksFragment");
            }else {
                mainFragment=MainFragment.newInstance();
                bookmarksfragment=BookmarksFragment.newInstance();
            }
            new BookmarksPresenter(MainActivity.this,bookmarksfragment);
    }
    

    看了上面的代码大概就能知道Activity的作用是什么了。主要是作为全局的控制,负责创建View以及Presenter实例,并将二者联系起来。

    View层的实现

       public class YourFragment extends Fragment implements YourContract.View {
    
        private YourContract.Presenter presenter;
        @Override
        public void onResume() {
            super.onResume();
            presenter.start();
        }
       @Override
           public void setPresenter(YourContract.Presenter presenter) {
               if (presenter!=null){
                   this.presenter=presenter;
               }
           }
      @Override
        public void showError() {
            Snackbar.make(fab, R.string.loaded_failed,Snackbar.LENGTH_INDEFINITE)
                    .setAction(R.string.retry, new View.OnClickListener() {
                        @Override
                        public void onClick(View v) {
                            presenter.reflush();
                        }
                    })
                    .show();
        }
    
      @Override
        public void showLoading() {
            refresh.post(new Runnable() {
                @Override
                public void run() {
                    refresh.setRefreshing(true);
                }
            });
        }
    
        @Override
        public void Stoploading() {
            refresh.post(new Runnable() {
                @Override
                public void run() {
                    refresh.setRefreshing(false);
                }
            });
        }
    }
    

    可以看到通过setPresenter方法获取到Presenter的实例。然后在Fragment的生命周期中调用presenter.start()方法。这样View层只负责数据给用户呈现他们看到的东西,而不去管具体是怎样实现的(ps:我觉得在onResume()方法之前调用应该都行吧,不知道是不是对的。如果有错,还请指教)

    Presenter层的实现

    public class YourPresenter implements YourContract.Presenter {
    public YourPresenter(Context context, YourContract.View view) {
            this.context=context;
            this.view=view;
            this.view.setPresenter(this);
        }
    
    @Override
        public void loatPosts(int PagerNum, final boolean cleaing) {
            //具体实现就不贴了,有点长
    }
    @Override
        public void start() {
            loatPosts(CurrentPagerNum,true);
        }
    
    @Override
        public void reflush() {
            loatPosts(CurrentPagerNum,true);
        }
    
    @Override
        public void loadMore(int PagerNum) {
            loatPosts(CurrentPagerNum+PagerNum,false);
        }
    }
    

    在构造方法中,Presenter将自身的事例传递给了View,这样View就能调用Presenter层的方法来处理业务逻辑了。在start()方法中,处理了数据加载。

    Model层的实现

    项目中model层最大的特点是被赋予了数据获取的职责,与我们平常model层只定义实体对象截然不同,实例中,数据的获取、存储、数据状态变化都是model层的任务,Presenter会根据需要调用该层的数据处理逻辑并在需要时将回调传入。这样model、presenter、view都只处理各自的任务,此种实现确实是单一职责最好的诠释。

    总结

    MVP的好处

    • 分离了视图逻辑和业务逻辑,降低耦合度,实现了Model和View真正的完全分离,可以修改View而不影响Modle
    • Activity 只处理生命周期的任务,代码变得更加简洁
    • 视图逻辑和业务逻辑分别抽象到了 View 和 Presenter 的接口中去,提高代码的可阅读性
    • View可以进行组件化。在MVP当中,View不依赖Model。这样就可以让View从特定的业务场景中脱离出来,可以说View可以做到对业务完全无知。它只需要提供一系列接口提供给上层操作。这样就可以做到高度可复用的View组件。
    • 利于测试驱动开发。Presenter 被抽象成接口,可以有多种具体的实现,所以方便进行单元测试。在使用MVP的项目中Presenter对View是通过接口进行,在对Presenter进行不依赖UI环境的单元测试的时候。可以通过Mock一个View对象,这个对象只需要实现了View的接口即可。然后依赖注入到Presenter中,单元测试的时候就可以完整的测试Presenter应用逻辑的正确性。

    以上就是我这段时间来对MVP的理解了。如果有错误的地方,欢迎指教哦。

    相关文章

      网友评论

        本文标题:MVP在Android中简易易懂的实现案例

        本文链接:https://www.haomeiwen.com/subject/zytigttx.html