来认识一下谷歌推荐的 MVP 写法

作者: d74f37143a31 | 来源:发表于2018-11-26 22:36 被阅读25次

    先说点题外话,最近报名了简书的日更计划,所以每天都会写一篇文章,字数不会太多,但是内容都会经过认真准备的,希望大家也能从中学到一些有用的知识,一起进步。

    今天介绍的是谷歌推荐的 MVP 写法,它的项目地址是 android-architecture

    我对它的主要代码结构做了一个截图,今天就下面这种图来认识一下规范的MVP写法。

    代码结构

    从图中我们可以看出它的代码结构是按功能模块来分包 数据是放在data下,工具类是放在utils下。

    它使用的 MVP 架构通过阅读源码可以知道是如下的架构:


    谷歌例子中的 MVP 架构

    根据上边的代码结构和截图,今天就从以下几个方面来分析:

    • 第一部分是数据部分在data包下
    • 第二部分是视图和中间人部分在对应的功能模块包下
    • 第三部分是BaseViewBasePresenter
    • 最后是阅读别人代码学到的一些编程思路

    第一部分也就是数据部分

    MVP 中的M表示的是Model也就是数据。查看谷歌这个例子中,它的model是这样去定义的,先定义model的接口,然后再定义接口的具体实现类。

    这也属于面向对象开发的思路,先把对象的属性和对应的方法,也就是先做设计,然后再去做具体的开发。

    简单贴一下例子中的源码,具体源码可点击查看

    接口定义
    public interface TasksDataSource {
    
        interface LoadTasksCallback {
    
            void onTasksLoaded(List<Task> tasks);
    
            void onDataNotAvailable();
        }
        ...
    
        void completeTask(@NonNull Task task);
    
        void completeTask(@NonNull String taskId);
    
        ...
    
        void deleteTask(@NonNull String taskId);
    }
    
    接口实现
    public class TasksRepository implements TasksDataSource {
        ...
        // Prevent direct instantiation.
        private TasksRepository(@NonNull TasksDataSource tasksRemoteDataSource,
                                @NonNull TasksDataSource tasksLocalDataSource) {
            mTasksRemoteDataSource = checkNotNull(tasksRemoteDataSource);
            mTasksLocalDataSource = checkNotNull(tasksLocalDataSource);
        }
        ...
    }
    

    Model中主要做的就是数据的增删改查操作,上面的代码中有一个checkNotNull方法,该方法在support包下也有,我这里贴一下源码看看源码中是如何判空的

    public static <T> T checkNotNull(T reference) {
      if (reference == null) {
        throw new NullPointerException();
      }
      return reference;
    }
    

    看完之后是不是觉得很简单,但是我们平时就是想不到,所以多阅读别人优秀的源码是多么重要啊。

    第二部分是视图(View)和 中间人(Presenter)

    在这里它使用了一个契约类 TasksContract来存放视图和中间人。这样做可以减少类的创建,便于管理和维护。在该契约类中主要是设计View要中要实现的方法和Presenter中要实现的方法,也属于面向对象中的先设计后再具体实现。

    简要代码如下,具体代码可点击查看

    public interface TasksContract {
    
        interface View extends BaseView<Presenter> {
    
            void setLoadingIndicator(boolean active);
              ...
            void showCompletedFilterLabel();
    
             ...
    
            void showFilteringPopUpMenu();
        }
    
        interface Presenter extends BasePresenter {
    
            void result(int requestCode, int resultCode);
    
             ...
    
            void completeTask(@NonNull Task completedTask);
    
              ...
    
            TasksFilterType getFiltering();
        }
    }
    

    设计完了接口下面就是具体的实现了。再贴一下开头的MVP结构图

    谷歌例子中的 MVP 架构

    再看一下 TasksPresenter的实现:这块是核心,理解了这里就基本上理解了这个例子中的 MVP 架构

    public class TasksPresenter implements TasksContract.Presenter {
        // 这里是 我们前面定义的 Model 具体实现
        private final TasksRepository mTasksRepository;
    
        // 这里是 我们前面定义的 View 接口
        private final TasksContract.View mTasksView;
    
        // 这里是 通过构造方法将 Model 和 View 注入到 Presenter 中
        public TasksPresenter(@NonNull TasksRepository tasksRepository, @NonNull TasksContract.View tasksView) {
            mTasksRepository = checkNotNull(tasksRepository, "tasksRepository cannot be null");
            mTasksView = checkNotNull(tasksView, "tasksView cannot be null!");
    
            // 这里通过接口注入的方式将 Presenter 注入到 View 中,这里的 setPresenter 是 BaseView 接口中的 方法
            mTasksView.setPresenter(this);
        }
    
          // 所以这就对应上了上图的框架结构图
              ...
        }
    
        ...
    
    }
    

    上面的代码中讲到了构造方法注入和接口注入,是为了实现类和类之间的解耦,用到的两种依赖注入手段。这个知识点我还在整理中过几天也会跟进完善。

    以上就是谷歌MVP例子的简单理解,网上有很多很详细的介绍博客,想深入了解的可以去搜一下。

    在阅读以上源码的时候我还发现了Activity中是不做UI操作的,都放到 Fragment 中去实现,我想了下,这可能也是一种好的开发习惯吧,因为Fragment在平板上兼容性会更好一些。网上说更好的方式是动态去添加 Fragment这里由于只有一个页面,所以它直接在activity 的 xml 文件中写死了Fragment

    这是它将Fragment添加到activity中的一个工具类,在添加之前还做了一层判空操作,真的是好习惯。

    public class ActivityUtils {
    
        /**
         * The {@code fragment} is added to the container view with id {@code frameId}. The operation is
         * performed by the {@code fragmentManager}.
         *
         */
        public static void addFragmentToActivity (@NonNull FragmentManager fragmentManager,
                                                  @NonNull Fragment fragment, int frameId) {
            checkNotNull(fragmentManager);
            checkNotNull(fragment);
            FragmentTransaction transaction = fragmentManager.beginTransaction();
            transaction.add(frameId, fragment);
            transaction.commit();
        }
    
    }
    

    而且它在Activity中获取该Fragment实例的时候又做了一次判空操作。如下:

    TasksFragment tasksFragment =
                    (TasksFragment) getSupportFragmentManager().findFragmentById(R.id.contentFrame);
            if (tasksFragment == null) {
                // Create the fragment
                tasksFragment = TasksFragment.newInstance();
                ActivityUtils.addFragmentToActivity(
                        getSupportFragmentManager(), tasksFragment, R.id.contentFrame);
            }
    

    文章到这里就结束了。

    阅读这个例子不仅看懂了 MVP架构,还学到了一些别人写代码的好习惯,以后得多多阅读别人优秀的代码了,加油!

    相关文章

      网友评论

        本文标题:来认识一下谷歌推荐的 MVP 写法

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