android开发之MVP模式

 我们平时开发最熟悉的模式,毋庸置疑,大家肯定都会说,MVC啊!V指的肯定是View层么,而C指的是Controller,指的一般都是Activity

但是,我们要明白,有时候Controller功能Activity并不能完全代替,而是Activity和View层一起承担这Controller功能,这就有时候导致了

一个控制页面中,有几千行代码,这样导致代码十分的臃肿了吧。所以,MVC模式并不太适合android开发,所以,google给出了一个更好

的框架模式,那就是MVP模式,下面让我们来看一下这个模式吧

MVP架构简介

  对于应用而言,我们每个项目都需要抽取出各个层面,在MVP模式中,我们将View层和数据层分离开来,这样,应用也分为

三个层次,

1,View层:View层也是视图层,View层只负责提供数据的展示,也就是我们所看到的跟用户交互的层面,即我们常说的Activity或者Fragmen层

2,Model层:这里的model层跟MVC中的Model不同,他所做的是负责对数据的存取,数据库的读写,网络层的请求等

3,Presenter层:对于这个层面,他的责任就是连接View层和Model层的桥梁并对业务逻辑进行处理,在MVP模式中,model层和View层无法直接联系

只能通过Presenter进行连接,在Presenter层中,从model层中获取数据,进行处理之后,然后再View层中进行显示这就使得Model

和View层进行隔离,使两个层面进行解耦,同时,将业务逻辑从View层中进行分离

通过图片来看这个架构工作原理



MVP架构的实现

首先我们从MVP架构的最内层开始分析,也就是对应的Model层。在这个项目中对应的data包下的内容。在data下对数据库等一些数据源的封装。对于Presenter层提供了TasksDataSource接口。在这里看一下这个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);

    ......
}
TasksDataSource接口的实现是TasksLocalDataSource,在TasksDataSource中的方法也就是一些对数据库的增删改查的操作。而在TasksDataSource的两个内部接口LoadTasksCallback和GetTaskCallback是Model层的回调接口。它们的真正实现是在Presenter层。对于成功获取到数据后变或通过这个回调接口将数据传递Presenter层。同样,若是获取失败同样也会通过回调接口来通知Presenter层。下面来看一下TasksDataSource的实现类。

public class TasksLocalDataSource implements TasksDataSource {

    ......

    @Override
    public void getTask(@NonNull String taskId, @NonNull GetTaskCallback callback) {

        //根据taskId查训出相对应的task
        ......

        if (task != null) {
            callback.onTaskLoaded(task);
        } else {
            callback.onDataNotAvailable();
        }
    }

    @Override
    public void saveTask(@NonNull Task task) {
        checkNotNull(task);
        SQLiteDatabase db = mDbHelper.getWritableDatabase();

        ContentValues values = new ContentValues();
        values.put(TaskEntry.COLUMN_NAME_ENTRY_ID, task.getId());
        values.put(TaskEntry.COLUMN_NAME_TITLE, task.getTitle());
        values.put(TaskEntry.COLUMN_NAME_DESCRIPTION, task.getDescription());
        values.put(TaskEntry.COLUMN_NAME_COMPLETED, task.isCompleted());

        db.insert(TaskEntry.TABLE_NAME, null, values);

        db.close();
    }

    ......
}

在这里我们针对任务的添加和编辑功能,所以省略很多代码。可以看出在TasksLocalDataSource中实现的getTask方法,在这个方法中传入TasksDataSource内的GetTaskCallback回调接口。在getTask方法的实现可以看出在查询到Task以后调用回调方法,若是在Presenter层中实现了这两个回调方法,便将数据传递到Presenter层。而对于查询到的Task为空的时候也是通过回调方法执行对应的操作。 
  同样对于通过网络请求获取到数据也是一样,对于成功请求到的数据可以通过回调方法将数据传递到Presenter层,对于网络请求失败也能够通过回调方法来执行相对应的操作。

Presenter与View层提供的接口

由于在Presenter和View层所提供的接口在一个类中,在这里就先来查看他们对外所提供了哪些接口。首先观察一下两个基类接口BasePresenter和BaseView。
BasePresenter
package com.example.android.architecture.blueprints.todoapp;

public interface BasePresenter {

    void start();

}

在BasePresenter中只存在一个start方法。这个方法一般所执行的任务是在Presenter中从Model层获取数据,并调用View接口显示。这个方法一般是在Fragment中的onResume方法中调用。

BaseView
package com.example.android.architecture.blueprints.todoapp;

public interface BaseView<T> {

    void setPresenter(T presenter);

}

  在BaseView中只有一个setPresenter方法,对于View层会存在一个Presenter对象。而setPresenter正是对View中的Presenter进行初始化。

AddEditTaskContract

在Android官方给出的MVP架构当中对于Presenter接口和View接口提供的形式与我们平时在网上所见的有所不同。在这里将Presenter中的接口和View的接口都放在了AddEditTaskContract类里面。这样一来我们能够更清晰的看到在Presenter层和View层中有哪些功能,方便我们以后的维护。下面就来看一下这个AddEditTaskContract类。

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层中则是对Task保存,更新等操作。

Presenter层的实现

下面就来看一下在Presenter是如何实现的。
public class AddEditTaskPresenter implements AddEditTaskContract.Presenter,
        TasksDataSource.GetTaskCallback {

    ......

    public AddEditTaskPresenter(@Nullable String taskId, @NonNull TasksDataSource tasksRepository,
            @NonNull AddEditTaskContract.View addTaskView) {
        mTaskId = taskId;
        mTasksRepository = checkNotNull(tasksRepository);
        mAddTaskView = checkNotNull(addTaskView);

        mAddTaskView.setPresenter(this);
    }

    @Override
    public void start() {
        if (mTaskId != null) {
            populateTask();
        }
    }

    ......

    @Override
    public void populateTask() {
        if (mTaskId == null) {
            throw new RuntimeException("populateTask() was called but task is new.");
        }
        mTasksRepository.getTask(mTaskId, this);
    }

    @Override
    public void onTaskLoaded(Task task) {
        // The view may not be able to handle UI updates anymore
        if (mAddTaskView.isActive()) {
            mAddTaskView.setTitle(task.getTitle());
            mAddTaskView.setDescription(task.getDescription());
        }
    }
    ......
}

在这里可以看到在AddEditTaskPresenter中它不仅实现了自己的Presenter接口,也实现了GetTaskCallback的回调接口。并且在Presenter中包含了Model层TasksDataSource的对象mTasksRepository和View层AddEditTaskContract.View的对象mAddTaskView。于是整个业务逻辑的处理就担负在Presenter的身上。 
  从Presenter的业务处理中可以看出,首先调用Model层的接口getTask方法,通过TaskId来查询Task。在查询到Task以后,由于在Presenter层中实现了Model层的回调接口GetTaskCallback。这时候在Presenter层中就通过onTaskLoaded方法获取到Task对象,最后通过调用View层接口实现了数据的展示。

View层的实现

 对于View的实现是在Fragment中,而在Activity中则是完成对Fragment的添加,Presenter的创建操作。下面首先来看一下AddEditTaskActivity类。

public class AddEditTaskActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.addtask_act);

        ......

        if (addEditTaskFragment == null) {
            addEditTaskFragment = AddEditTaskFragment.newInstance();
            ......
            ActivityUtils.addFragmentToActivity(getSupportFragmentManager(),
                    addEditTaskFragment, R.id.contentFrame);
        }

        // Create the presenter
        new AddEditTaskPresenter(
                taskId,
                Injection.provideTasksRepository(getApplicationContext()),
                addEditTaskFragment);
    }

    ......
}

 对于Activity的提供的功能也是非常的简单,首先创建Fragment对象并将其添加到Activity当中。之后创建Presenter对象,并将Fragment也就是View传递到Presenter中。
下面再来看一下View的实现,也就是Fragment。
public class AddEditTaskFragment extends Fragment implements AddEditTaskContract.View {

    ......

    @Override
    public void onResume() {
        super.onResume();
        mPresenter.start();
    }

    @Override
    public void setPresenter(@NonNull AddEditTaskContract.Presenter presenter) {
        mPresenter = checkNotNull(presenter);
    }

    ......

    @Override
    public void setTitle(String title) {
        mTitle.setText(title);
    }

    ......
}

 在这对于源码就不在过多贴出。在Fragment中,通过setPresenter获取到Presenter对象。并通过调用Presenter中的方法来实现业务的处理。而在Fragment中则只是对UI的一些操作。这样一来对于Fragment类型的代码减少了很多,并且逻辑更加清晰。 
  我们注意到View层的实现是通过Fragment来完成的。对于View的实现为什么要采用Fragment而不是Activity。来看一下官方是如何解释的。

The separation between Activity and Fragment fits nicely with this implementation of MVP: the Activity is the overall controller that creates and connects views and presenters.
Tablet layout or screens with multiple views take advantage of the Fragments framework.
    
    
  • 1
  • 2
在这里官方对于采用Fragment的原因给出了两种解释。


  • 通过Activity和Fragment分离非常适合对于MVP架构的实现。在这里将Activity作为全局的控制者将Presenter于View联系在一起。
  • 采用Fragment更有利于平板电脑的布局或者是多视图屏幕。

总结

通过MVP架构的使用可以看出对于各个层次之间的职责更加单一清晰,同时也很大程度上降低了代码的耦合度。对于官方MVP架构示例,google也明确表明对于他们所给出的这些架构示例只是作为参考,而不是一个标准。所以对于基础的MVP架构有更大的扩展空间。例如综合google给出的示例。我们可以通过在MVP架构的基础上使用dagger2,rxJava等来构建一个Clean架构。也是一个很好的选择。


我相信,这位朋友写的这篇博客很多人都是看的云里雾里的,但是,确实总结的很好,那么,我补充一下吧,写了一个小小的demo来总结一下mvp这个框架吧

http://download.csdn.net/download/u011228868/10218249
这是一个小小的demo,多说一句,小项目实在不适合这个框架,但是,大项目确实有点非常的多,解耦性非常强,分块开发方便,代码简单,通俗易懂,接口清晰




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值