MVP入门

原创 2016年05月31日 16:37:36

基础

        官方demo

        mvc中,c指的是activity等,但它同时又承担了一部分的v工作,显得混乱冗长。而mvp中,将v当作activity,新添加一层做为p,m层不变。

        对于官方的demo,整体包结构采用的是按模板划分包的。如下:


        其中util指的是一些工具类,data指的是数据层(m层),本demo中使用到bean也是定义在这里的。其余的就是一些各个任务模板。

        总结每一个任务模板可以看出:每一个模板中有activity,presenter,contract,fragment四个类以及一些额外的本模板需要的类。各类的功能如下。

整体架构如下:


分类详解

contract

        合同。定义了本模板中m与p层,p层与v层之间的接口。如下:

public interface AddEditTaskContract {

    interface View extends BaseView<Presenter> {

        void showEmptyTaskError();

        void showTasksList();

        void setTitle(String title);

        void setDescription(String description);

        boolean isActive();
    }

    interface Presenter extends BasePresenter {

        void createTask(String title, String description);

        void updateTask(String title, String description);

        void populateTask();
    }
}
       当然,也可以将该类省略,将其中的View与Presenter单独生成两个接口类。只不过放在一起,有助于一次性了解各个模板之间的接口、交互。

presenter

        即p层,用于具体处理业务逻辑。也是m,v层交互的桥梁。并且,由于p的初始化并不是在v中,所以在p的构造函数中需要为v指定当前p是哪个。如下:

    public StatisticsPresenter(@NonNull TasksRepository tasksRepository,
                               @NonNull StatisticsContract.View statisticsView) {
        mTasksRepository = checkNotNull(tasksRepository, "tasksRepository cannot be null");
        mStatisticsView = checkNotNull(statisticsView, "StatisticsView cannot be null!");

        mStatisticsView.setPresenter(this);
    }

        调用了v层的setPresenter,为v层指定p。

fragment

        即v层。

activity

        本demo中,所有的v层都是由fragment实现的,而activity是为p指定m与v的地方。如:

        new StatisticsPresenter(
                Injection.provideTasksRepository(getApplicationContext()), statisticsFragment);

        从这里可以看出,p层关联的v,m都是在activity中指定的,而不是直接在fragment中直接new一个p。只不过所有的v都需要有一个setPresenter()方法,用于指定该v关联的p是哪个。这个方法的调用在p的构造函数中。

BaseView

        很简单,只有一个用来指定p的方法。这是因为所有的p都不是在v中进行的初始化,所以任何一个v都必须得具有设置P的方法。如下:

public interface BaseView<T> {

    void setPresenter(T presenter);

}

BasePresenter

        它是将所有p的共性操作都抽取出来的。如下:

public interface BasePresenter {

    void start();

}
        start()用于开始加载数据,可以发现在fragment中onResume中会调用该方法。

        其实,还应该有一个end()方法,用于将各个层的引用删除,防止出现内存泄露。

Model

        对于一个应用来说,数据的来源往往有两个部分:本地以及服务器。而p层是往往不需要关注于数据到底来源于哪种渠道,它只需要有一个m层的引用,并可通过该引用拿到v层需要的数据即可,所以这里使用了代理模式。在代理类中对数据的来源进行了分类,并可以进行缓存。两个数据来源往往具有相同的操作,因此可抽象成一个接口。具体代码结构如下:

        data包统筹数据,source包为数据来源,Task为数据的bean。local表示本地数据,remote为远程数据(服务器)。
        对于本地数据的处理,采用了数据库——Contract定义的数据库字段名,Helper为数据库的helper,localdatasource为对数据库的操作。
        TasksDataSource为数据操作的总接口,localdatasource与remotedatasource都实现了它。TasksRepository也实现了它。
        对于P层来说,它应该只持有一个引用,通过该对象可以拿到数据,而不用判断这数据是来源于网络还是本地。因此,需要将local与remote进行统合,并将统合后的对象提供给p层,同时这个对象应该与local,remote实现相同的接口——因为三者在功能上是类似的,只不过统合对象对后两者进行了包装、分发。p层使用统合对象进行获取数据,具体的数据来源应该在m层中进行
        因此,使用TasksRepository对local与remote进行了统合,p层拿到的对象就是它的对象。其内部对数据的来源进行了判断。如下:
    public void getTasks(@NonNull final LoadTasksCallback callback) {
        checkNotNull(callback);

        // Respond immediately with cache if available and not dirty
        if (mCachedTasks != null && !mCacheIsDirty) {
            callback.onTasksLoaded(new ArrayList<>(mCachedTasks.values()));
            return;
        }

        if (mCacheIsDirty) {
            // If the cache is dirty we need to fetch new data from the network.
            getTasksFromRemoteDataSource(callback);
        } else {
            // Query the local storage if available. If not, query the network.
            mTasksLocalDataSource.getTasks(new LoadTasksCallback() {
                @Override
                public void onTasksLoaded(List<Task> tasks) {
                    refreshCache(tasks);
                    callback.onTasksLoaded(new ArrayList<>(mCachedTasks.values()));
                }

                @Override
                public void onDataNotAvailable() {
                    getTasksFromRemoteDataSource(callback);
                }
            });
        }
    }
    private void getTasksFromRemoteDataSource(@NonNull final LoadTasksCallback callback) {
        mTasksRemoteDataSource.getTasks(new LoadTasksCallback() {
            @Override
            public void onTasksLoaded(List<Task> tasks) {
                refreshCache(tasks);//刷新内存缓存
                refreshLocalDataSource(tasks);//刷新到数据库中
                callback.onTasksLoaded(new ArrayList<>(mCachedTasks.values()));
            }

            @Override
            public void onDataNotAvailable() {
                callback.onDataNotAvailable();
            }
        });
    }
        从上述代码可以看出,在local、remote与p层之间添加了一个缓存区。

总结

        1,使用Activity充当p,m,v直接建立联系的桥梁。使用fragment充当v。
        2,对于model层,统一类管理数据的来源,不应将获取本地还是远程的判断放在p层中。并且,为提高访问速度,可能需要对数据进行内存缓存。
        3,如果Activity的逻辑比较复杂,应使用MVP,如果比较简单可直接写在activity中。

扩展

        上述示例中,数据的加载没有考虑在子线程中操作。可以考虑在m与p之前添加一个loader,由loader子线程中通过TasksRepository加载数据。demo来源。这个demo中主要采用的是mvp+loader+观察者模式。另参考Loader
        Loader:只负责提供子线程,供加载数据的方法运行在子线程中。
        TasksRepository:为本地数据和网络数据提供代理,以及对加载到的数据提供缓存。
        开始方法应该是p的start(),这里面直接调用了LoaderManager#initLoader(),也就是启动了Loader进行数据的加载。如下:
    protected void onStartLoading() {
        // Deliver any previously loaded data immediately if available.
        if (mRepository.cachedTasksAvailable()) {
            deliverResult(mRepository.getCachedTasks());//将缓存数据给返回
        }

        // Begin monitoring the underlying data source.
        mRepository.addContentObserver(this);

        if (takeContentChanged() || !mRepository.cachedTasksAvailable()) {
            // When a change has  been delivered or the repository cache isn't available, we force
            // a load.
            forceLoad();
        }
    }
        逻辑很简单,有缓存数据就返回缓存数据,没有就forceLoad。因为继承的是AsyncTaskLoader,所以forceLoad会调用起doInBackground()。这个方法实现也很简单,直接使用mRepository.getTasks()。这就将数据的加载过程放到了子线程中。
        数据加载结束后,对数据进行分类,并通知view进行展示。略。
        在v中有刷新的操作,该操作会调用p的loadTasks(),它又会调用mRepository.refreshTasks(),该方法只是通知它里面的观察者调用onTasksChanged()方法。在上面的onStartLoading()中可以发现,loader是repository的一个观察者,所以最终会调用到loader的onTasksChanged(),它里面只是一个判断并调用forceLoad()。这就是观察者模式,同时也保证了数据的加载永远走在子线程中。

        




        



        


版权声明:本文为博主原创文章,未经博主允许不得转载。

Android——MVP架构模式之入门demo

什么是MVP?MVP(Model - View - Presenter , 模型 - 视图 - 表示器)模式则是由IBM开发出来的一个针对C++和java的编程模型,大概出现于2000年,是MVC模式...

MVP android的入门小例子

  • 2014年05月02日 20:27
  • 2.79MB
  • 下载

MVP入门示例

  • 2017年03月01日 15:16
  • 18.66MB
  • 下载

Android入门学习——Retrofit+MVP模式学习

Android入门学习——Retrofit+MVP模式学习简单使用最近闲着无聊的时候在网上随意闲逛的时候,Retrofit、RxJava、RxAndroid这几个词,顺带着okHttp、MVP出现的频...
  • hdszlk
  • hdszlk
  • 2016年05月10日 22:19
  • 1215

Android使用mvp模式入门

  • 2016年03月05日 14:57
  • 8.03MB
  • 下载

MVP入门demo案例

  • 2016年08月23日 11:17
  • 17.48MB
  • 下载

Android MVP从懵逼到入门:登陆业务实践

我的简书原文链接MVP简介最近几天在啃MVP,现在的你或许跟几天前的我一样,对MVP还是一脸懵逼,虽然MVP三个字母都认识,但连在一起却不明白到底是个什么东东,没关系,快来干了这碗鸡汤,立马从懵逼到入...

MVP入门Demo

  • 2016年07月19日 17:48
  • 22.05MB
  • 下载

Android平台MVP开发模式实战入门

上一篇文章,我们使用android开源框架LitePal新建了一个android端数据库userinf.db,本篇我们在已建立的数据库上写一个简单的mvp模式的demo。第一步, 了解一下mvp模式我...

Android框架模式(1)-MVP入门

转载自远古大钟的博客(http://blog.csdn.net/duo2005duo) 简介 MVP是MVC的衍生版本,跟MVC类似,但是在Android中更适用,也分三层:    M...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:MVP入门
举报原因:
原因补充:

(最多只允许输入30个字)