×

浅析MVP中model层设计【从零开始搭建android框架系列(7)】

96
安东尼_Anthony 595a1b60 08f6 4beb 998f 2bf55e230555
2016.06.09 16:13* 字数 2045

原文链接:Anthony的简书博客
推荐链接:安卓架构文章合集

1 前言

在本系列文章从零开始搭建android框架系列之前我多次提到了官方mvp项目的构建。并应用到了项目MVPCommon中。但是细心的你肯定都会发现,之前的文章都在整体上对MVP 的使用进行了说明,却对其中的model层一言带过。包括数据也是大多采用假数据。

使用了MVP,我们肯定不会再像以前网络访问数据,SharedPreference保存数据,本地数据库保存,缓存数据等的处理分散于每个activity或者fragment之间。数据的获取、存储、数据状态变化都将是Model层的任务。RxJava,Retrofit,EventBus,SqlBrite等技术都会在后续得到分析和使用。

下面的文章将会从一些优秀的模板代码分析出发,研究MVP中model层的设计,后续的文章将会在这些基础上继续分析和使用。

这里整理网络上也有不少关于这方面优秀文章,将会在参考资料中给出。


2 google官方mvp中model层设计

之前两篇文章分别对官方google官方架构MVP解析与实战Google官方MVP+Dagger2架构详解项目进行了解析,并没有对其中的model层进行分析,这里单独抽取出来和大家共同学习一下。其实该项目中Model层最大的特点是被赋予了数据获取的职责,与我们平常Model层只定义javabean,实体对象截然不同。实例中,数据的获取、存储、数据状态变化都是Model层的任务,Presenter会根据需要调用该层的数据处理逻辑并在需要时将回调传入。

我们来看TaskDetailPresenter 的 start() 方法:

 @Override
    public void start() {
        openTask();
    }

    private void openTask() {
        // 判空处理
        if (null == mTaskId || mTaskId.isEmpty()) {
            mTaskDetailView.showMissingTask();
            return;
        }
        // 更新状态
        mTaskDetailView.setLoadingIndicator(true);
        // 获取该条Task数据
        mTasksRepository.getTask(mTaskId, new TasksDataSource.GetTaskCallback() {
            @Override
            public void onTaskLoaded(Task task) {
                // The view may not be able to handle UI updates anymore
                // View已经被用户回退
                if (!mTaskDetailView.isActive()) {
                    return;
                }
                // 获取到task数据,并更新UI
                mTaskDetailView.setLoadingIndicator(false);
                if (null == task) {
                    mTaskDetailView.showMissingTask();
                } else {
                    showTask(task);
                }
            }

            @Override
            public void onDataNotAvailable() {
                // The view may not be able to handle UI updates anymore
                // 显示数据获取失败时的状态
                if (!mTaskDetailView.isActive()) {
                    return;
                }
                mTaskDetailView.showMissingTask();
            }
        });
    }

可以看到的是presenter中调用了mTaskDetailView.setLoadingIndicator(true);中更新view层的状态之后,接着调用mTasksRepository.getTask......获取数据,并且在数据获取成功和失败后,分别处理回调方法onTaskLoaded,onDataNotAvailable,并在其中更新view。将数据的操作完全交给TasksRepository处理。

我们接着看 TasksRepository 中的getTask() 方法,

@Singleton
public class TasksRepository implements TasksDataSource {
......
    private final TasksDataSource mTasksRemoteDataSource;

    private final TasksDataSource mTasksLocalDataSource;

    @Inject
    TasksRepository(@Remote TasksDataSource tasksRemoteDataSource,
            @Local TasksDataSource tasksLocalDataSource) {
        mTasksRemoteDataSource = tasksRemoteDataSource;
        mTasksLocalDataSource = tasksLocalDataSource;
    }
 ......

    /**
     * Gets tasks from local data source (sqlite) unless the table is new or empty. In that case it
     * uses the network data source. This is done to simplify the sample.
     */
    @Override
    public void getTask(@NonNull final String taskId, @NonNull final GetTaskCallback callback) {
        checkNotNull(taskId);
        checkNotNull(callback);

        Task cachedTask = getTaskWithId(taskId);

        // 缓存获取数据,并回调
        if (cachedTask != null) {
            callback.onTaskLoaded(cachedTask);
            return;
        }

        // 本地获取数据
        mTasksLocalDataSource.getTask(taskId, new GetTaskCallback() {
            @Override
            public void onTaskLoaded(Task task) {
                callback.onTaskLoaded(task);
            }

            @Override
            public void onDataNotAvailable() {
            //本地获取数据失败,使用网络数据
                mTasksRemoteDataSource.getTask(taskId, new GetTaskCallback() {
                    @Override
                    public void onTaskLoaded(Task task) {
                        callback.onTaskLoaded(task);
                    }

                    @Override
                    public void onDataNotAvailable() {
                        callback.onDataNotAvailable();
                    }
                });
            }
        });
    }

   
}

我们发现 TasksRepository 维护了两个数据源,一个是本地mTasksLocalDataSource,一个是远程mTasksRemoteDataSource。从getTask方法中可以看到这里首先从缓存获取数据,如果获取成功则直接回调·callback.onTaskLoaded(cachedTask);`,接着从本地获取,获取失败后再从网络获取 ,这也和处理图片的三级缓存策略一样。

我们发现TasksRepository类都实现了 TasksDataSource 接口:

public interface TasksDataSource {

    interface LoadTasksCallback {

        void onTasksLoaded(List<Task> tasks);

        void onDataNotAvailable();
    }

    interface GetTaskCallback {

        void onTaskLoaded(Task task);

        void onDataNotAvailable();
    }

    void getTasks(@NonNull LoadTasksCallback callback);

    void getTask(@NonNull String taskId, @NonNull GetTaskCallback callback);

    void saveTask(@NonNull Task task);

    void completeTask(@NonNull Task task);

    void completeTask(@NonNull String taskId);

    void activateTask(@NonNull Task task);

    void activateTask(@NonNull String taskId);

    void clearCompletedTasks();

    void refreshTasks();

    void deleteAllTasks();

    void deleteTask(@NonNull String taskId);
}

可以发现我们正是在这里TasksDataSource中定义了内部接口GetTaskCallback。从而实现TasksRepository中数据获取的时候的回调。

为了简化整个模板代码的操作,这里只在getTasks()getTask()方法中添加了回调并不是说其他地方不需要数据的回调。

还记得之前的Google官方MVP+Dagger2架构详解文章讲解的整个实例app的TasksRepositoryComponent在整个应用的Application中初始化。

@Singleton
@Component(modules = {TasksRepositoryModule.class, ApplicationModule.class})
public interface TasksRepositoryComponent {
    TasksRepository getTasksRepository();
}

我们可以看到我们将 TasksRepositoryModule放在mock下,做mock测试,看下图:


我们也在TasksRepositoryModule中提供了数据的实例,也正是在mock中提供了假数据FakeTasksRemoteDataSource替换了TasksRemoteDataSource.

@Module
public class TasksRepositoryModule {

    @Singleton
    @Provides
    @Local
    TasksDataSource provideTasksLocalDataSource(Context context) {
        return new TasksLocalDataSource(context);
    }

    @Singleton
    @Provides
    @Remote
    TasksDataSource provideTasksRemoteDataSource() {
        return new FakeTasksRemoteDataSource();
    }
}

到这里也就完成了整个数据model层的提供,我们也可以看到Dagger2再次显示了它的威力。

总结:

最后,我们再来看这张图。Fragment作为View,View和Presenter通过Activity来进行关联,Presenter对数据的调用是通过TasksRepository来完成的,而TasksRepository维护着它自己的数据源和实现。


到这里你肯定明白了为什么我们需要Repository层了---->屏蔽底层细节。
上层(activity/fragment/presenter)不需要知道数据的细节(或者说 - 数据源),来自于网络、数据库,亦或是内存等等。如此,一来上层可以不用关心细节,二来底层可以根据需求修改,不会影响上层,两者的分离用可以帮助协同开发。

3 android-boilerplate 中model层的设计

如果你关注安卓架构,之前肯定关注过Android Application Architecture(翻译文章 Android应用架构- 小鄧子的简书)这篇文章。其中的示例项目android-boilerplate也是一个基于MVP架构的框架,加入EventBus(Otto),RxJava,Retrofit,SqlBrite等。其中的model层有很多值得借鉴的地方。这里再从这整个架构图来学习一下相应的思路。

View(视图)层:Activities,Fragment以及ViewGroup等在这一层。处理用户的交互和输入事件,并且触发Presenter中的相应操作。

Presenter层 :Presenters 订阅(subscibe) RxJava的Observables,负责处理订阅周期,处理由DataManager提供的数据,并调用View层中的相应方法展示数据。

Model (数据)层: 负责获取,保存,缓存以及修改数据。负责与本地数据库,其他数据存储,restful APIs,以及第三方SDKs 交互。在此架构中,Model层被划分为两个部分:许多helpers类和一个 DataManager.helpers类的数量在不同的工程中不尽相同,但是每个都有自己的功能。比如:通过SharedPreferences与数据进行交互的PreferHelper,通过SqlBrite提供与数据库交互的DatabaseHelper,DataManager结合并且转化不同的Helpers类为Rx操作符,向Presenter层提供Observables类型的数据(provide meaningful data to the Presenter),并且同时处理数据的并发操作(group actions that will always happen together.)。这一层也包含实际的model类,用于定义当前数据架构。

因此,我们可以看到的优点是:
1 Activity和Fragment变得非常轻量。他们唯一的职责就是建立/更新UI和处理用户事件。因此,他们变得更容易维护。

2 RxJava的Observable和操作符避免了嵌套回调的出现,同时如果数据model层出现数据错误,我们也会在presenter层得到处理,在presenter层调用相应的view层的方法显示错误信息。

3 现在我们通过模拟View Layer可以很容易的编写出单元测试。之前这些代码是View Layer的一部分,所以我们很难对它进行单元测试。整个架构变得测试友好。

4 如果DataManager变得臃肿,我们可以通过转移一些代码到Presenter来缓解这个问题。

5 通过引入Event Bus (事件总线,这个项目使用的是otto)。它允许我们在Data Layer中发送事件,以便View Layer中的多个组件都能够订阅到这些事件。比如DataManager中的退出登录方法可以发送一个事件,订阅这个事件的多个Activity在接收到该事件后就能够更改它们的UI视图,从而显示一个登出状态。

6 这里的DataManager也扮演了官方示例项目中Respository的作用,屏蔽底层细节。

7 引入Dagger2 ,添加依赖注入,实现组件的重用,也就使得测试变得更加容易.

未完待续......

参考链接:
1 从零开始的Android新项目5 - Repository层(上) Retrofit、Repository组装
2 从零开始的Android新项目6 - Repository层(下) Realm、缓存、异常处理
3 android architecture architecture guidelines
4 Android应用架构- 小鄧子的简书
5 Android官方MVP架构项目解析
6 从零开始搭建android框架系列
7 google官方架构MVP解析与实战
8 Google官方MVP+Dagger2架构详解
9 Android Application Architecture
10 完美的安卓 model 层架构(上)
11 完美的安卓 model 层架构(下)

Web note ad 1