当前位置: 移动技术网 > 移动技术>移动开发>Android > 浅谈Android官方MVP架构解读

浅谈Android官方MVP架构解读

2019年07月24日  | 移动技术网移动技术  | 我要评论

综述

对于mvp (model view presenter)架构是从著名的mvc(model view controller)架构演变而来的。而对于android应用的开发中本身可视为一种mvc架构。通常在开发中将xml文件视为mvc中的view角色,而将activity则视为mvc中的controller角色。不过更多情况下在实际应用开发中activity不能够完全充当controller,而是controller和view的合体。于是activity既要负责视图的显示,又要负责对业务逻辑的处理。这样在activity中代码达到上千行,甚至几千行都不足为其,同时这样的activity也显得臃肿不堪。所以对于mvc架构并不很合适运用于android的开发中。下面就来介绍一下mvp架构以及看一下google官方给出的mvp架构示例。

mvp架构简介

对于一个应用而言我们需要对它抽象出各个层面,而在mvp架构中它将ui界面和数据进行隔离,所以我们的应用也就分为三个层次。

  • view: 对于view层也是视图层,在view层中只负责对数据的展示,提供友好的界面与用户进行交互。在android开发中通常将activity或者fragment作为view层。
  • model: 对于model层也是数据层。它区别于mvc架构中的model,在这里不仅仅只是数据模型。在mvp架构中model它负责对数据的存取操作,例如对数据库的读写,网络的数据的请求等。
  • presenter:对于presenter层他是连接view层与model层的桥梁并对业务逻辑进行处理。在mvp架构中model与view无法直接进行交互。所以在presenter层它会从model层获得所需要的数据,进行一些适当的处理后交由view层进行显示。这样通过presenter将view与model进行隔离,使得view和model之间不存在耦合,同时也将业务逻辑从view中抽离。

下面通过mvp结构图来看一下mvp中各个层次之间的关系。

在mvp架构中将这三层分别抽象到各自的接口当中。通过接口将层次之间进行隔离,而presenter对view和model的相互依赖也是依赖于各自的接口。这点符合了接口隔离原则,也正是面向接口编程。在presenter层中包含了一个view接口,并且依赖于model接口,从而将model层与view层联系在一起。而对于view层会持有一个presenter成员变量并且只保留对presenter接口的调用,具体业务逻辑全部交由presenter接口实现类中处理。

官方mvp架构分析

项目介绍

对于mvp架构有了一些的了解,而在前端时间google给出了一些app开发架构的实现。

项目地址为:.

在这里进入readme看一下这次google给出那些android开发架构的实现。

对于上面五个开发架构的实现表示到目前为止是已经完成的项目,而下面两个则表示正在进行的中的项目。现在首先来介绍一下这几个架构。

  • todo-mvp: 基础的mvp架构。
  • todo-mvp-loaders:基于mvp架构的实现,在获取数据的部分采用了loaders架构。
  • todo-mvp-databinding: 基于mvp架构的实现,采用了数据绑定组件。
  • todo-mvp-clean: 基于mvp架构的clean架构的实现。
  • todo-mvp-dagger2: 基于mvp架构,采用了依赖注入dagger2。
  • dev-todo-mvp-contentproviders: 基于mvp-loaders架构,使用了contenpproviders。
  • dev-todo-mvp-rxjava: 基于mvp架构,对于程序的并发处理和数据层(mvp中的model)的抽象。

从上述的介绍中可以看出,对于官方给出所有的架构的实现最终都是基于mvp架构。所以在这里就对上面的基础的mvp架构todo-mvp进行分析。

项目结构的分析

对于这个项目,它实现的是一个备忘录的功能。对于工作中未完成的任务添加到待办任务列表中。我们能够在列表中可以对已完成的任务做出标记,能够进入任务详细页面修改任务内容,也能够对已完成的任务和未完成的任务数量做出统计。

首先在这里来看一下todo-mvp整体的项目结构

从上图中可以看出,项目整体包含了一个app src目录,四个测试目录。在src目录下面对代码的组织方式是按照功能进行划分。在这个项目中包含了四个功能,它们分别是:任务的添加编辑(addedittask),任务完成情况的统计(statistics),任务的详情(taskdetail),任务列表的显示(tasks)。对于data包它是项目中的数据源,执行数据库的读写,网络的请求操作都存放在该包内,也是mvp架构中的model层。而util包下面则是存放一些项目中使用到的工具类。在最外层存放了两接口basepresenter和baseview。它们是presenter层接口和view层接口的基类,项目中所有的presenter接口和view层接口都继承自这两个接口。

现在进入功能模块内看下在模块内部对类是如何划分的。在每个功能模块下面将类分作xxactivity,xxfragment,xxpresenter,xxcontract。也正是这些类构成了项目中的presenter层与view层。下面就来分析在这个项目中是如何实现mvp架构。

mvp架构的实现

在这里只从宏观上关注mvp架构的实现,对于代码的内部细节在就不在具体分析。那么就以任务的添加和编辑这个功能来看一下android官方是如何实现mvp架构。

model层的实现

首先我们从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.

在这里官方对于采用fragment的原因给出了两种解释。

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

总结

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

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持移动技术网。

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网