本文主要介绍Android官方MVP架构的解读。边肖认为这很好。现在分享给大家,给大家一个参考。来和边肖一起看看吧。
综述
MVP(模型视图呈现器)架构是从著名的MVC(模型视图控制器)架构发展而来的。Android应用的开发本身可以看作是一个MVC架构。一般来说,XML文件在MVC中被视为视图角色,而Activity在MVC中被视为控制器角色。但更多情况下,Activity在实际应用开发中并不能完全充当控制器,而是控制器和视图的结合体。因此,Activity不仅负责视图的显示,还负责业务逻辑的处理。这样,Activity中有几千行代码,甚至几千行都不够,这个Activity就臃肿了。所以MVC架构并不适合Android开发。先介绍一下MVP架构,看看google给出的MVP架构的例子。
MVP架构简介
对于一个应用程序,我们需要抽象它的所有层次。在MVP架构中,它将UI界面与数据分离,所以我们的应用分为三个层次。
视图:视图层也就是视图层,在其中只负责数据显示,提供友好的界面与用户交互。Android开发中通常使用Activity或Fragment作为视图层。
模型:模型层也是数据层。它不同于MVC架构中的模型,这里不仅仅是数据模型。在MVP架构中,Model负责访问数据,比如读写数据库,请求网络数据等。
表示层:对于表示层来说,它是视图层和模型层之间的桥梁,处理业务逻辑。在MVP架构中,模型和视图不能直接交互。因此,在呈现层,它会从模型层获取所需的数据,经过适当的处理后,呈现到视图层进行显示。这样,演示者就把视图和模型分离了,这样视图和模型之间就没有耦合,同时业务逻辑也和视图分离了。
我们通过MVP结构图来看看MVP中各层级之间的关系。
在MVP架构中,这三层被抽象成各自的接口。层由接口分隔,而呈现者对视图和模型的相互依赖也依赖于它们各自的接口。这符合接口隔离的原则,也是面向接口的编程。表示层包含一个视图接口,并依赖于模型接口,该接口将模型层与视图层链接起来。视图层将保存一个Presenter成员变量,只保留对Presenter接口的调用,所有具体的业务逻辑将由Presenter接口实现类处理。
官方MVP架构分析
项目介绍
我对MVP架构有一些了解,Google在前端时间给出了一些App开发架构的实现。
项目地址为:https://github.com/googlesamples/android-architecture.
在这里上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架构,使用contentproviders。
Dev-todo-mvp-rxjava:基于mvp架构,抽象出程序的并发处理和数据层(MVP中的模型)。
从上面的介绍可以看出,所有的官方架构最终都是基于MVP架构。所以在这里,对上面的基本MVP架构todo-mvp进行分析。
项目结构的分析
对于这个项目,它实现了备忘录的功能。将未完成的任务添加到待办事项列表中。我们可以在列表中标记已完成的任务,进入任务详情页面修改任务内容,统计已完成任务数和未完成任务数。
首先,我们来看看todo-mvp的整体项目结构。
从上图可以看出,项目整体包括一个app src目录和四个测试目录。代码根据其功能组织在src目录中。本项目有四个功能,分别是:addedittask、任务完成情况统计、任务明细和任务列表显示。至于数据包,它是项目中的数据源,读写数据库,所有的网络请求都存储在包中,也是MVP架构中的模型层。在util包下,有一些项目中使用的工具类。两个接口BasePresenter和BaseView存储在最外层。它们是Presenter接口和View接口的基类,项目中的所有Presenter接口和View接口都继承自这两个接口。
现在进入功能模块,看看模块内部的类是如何划分的。在每个功能模块下,类分为xxactivity、xxfragment、xxpresenter和xxcontract。正是这些类构成了项目中的演示者层和视图层。下面我们来分析一下如何在这个项目中实现MVP架构。
MVP架构的实现
这里只从宏观角度关注MVP架构的实现,不具体分析代码内部细节。那么,我们来看看Android官方是如何实现MVP架构的,具有添加和编辑任务的功能。
Model层的实现
首先从MVP架构最里面的一层开始,也就是对应的模型层。该项目中相应数据包下的内容。数据下的数据库等一些数据源的封装。为Presenter层提供了TasksDataSource接口。在这里看一下这个TasksDataSource接口。
公共接口任务数据源{
接口加载任务回调{
void onTasksLoaded(ListTask任务);
void ondatanovailable();
}
接口GetTaskCallback {
void onTaskLoaded(任务Task);
void ondatanovailable();
}
void get tasks(@ NonNull LoadTasksCallback回调);
void getTask(@非空字符串taskId,@非空GetTaskCallback回调);
void save Task(@ NonNull Task Task);
.
}
TaskDataSource接口的实现是TasksLocalDataSource,TaskDataSource中的方法是一些数据库添加、删除、查询的操作。而TasksDataSource的两个内部接口LoadTasksCallback和GetTaskCallback是模型层的回调接口。它们的真正实现是在表示层。用于通过这个回调接口成功的数据采集或数据传输到表示层。同样,如果采集失败,也将通过回调接口通知表示层。让我们来看看TasksDataSource的实现类。
公共类TasksLocalDataSource实现TasksDataSource {
.
@覆盖
public void getTask(@ NonNull String taskId,@ NonNull GetTaskCallback callback){
//根据taskId找到对应的任务
.
如果(任务!=null) {
callback . ontaskloaded(task);
}否则{
callback . ondatanovailable();
}
}
@覆盖
public void save Task(@ NonNull Task Task){
checkNotNull(任务);
SQLiteDatabase db=MDB helper . getwritabledatabase();
content values values=new content values();
values.put(TaskEntry。COLUMN_NAME_ENTRY_ID,task . getid());
values.put(TaskEntry。COLUMN_NAME_TITLE,task . gettitle());
values.put(TaskEntry。COLUMN_NAME_DESCRIPTION,task . get DESCRIPTION());
values.put(TaskEntry。COLUMN_NAME_COMPLETED,task . is COMPLETED());
db.insert(TaskEntry。TABLE_NAME,null,values);
db . close();
}
.
}
在这里我们针对任务的添加和编辑功能,所以省略很多代码。可以看出在TasksLocalDataSource中实现的获取任务方法,在这个方法中传入任务数据源内的GetTaskCallback回调接口。在获取任务方法的实现可以看出在查询到工作以后调用回调方法,若是在提出者层中实现了这两个回调方法,便将数据传递到提出者层。而对于查询到的工作为空的时候也是通过回调方法执行对应的操作。
同样对于通过网络请求获取到数据也是一样,对于成功请求到的数据可以通过回调方法将数据传递到提出者层,对于网络请求失败也能够通过回调方法来执行相对应的操作。
Presenter与View层提供的接口
由于在提出者和视角层所提供的接口在一个类中,在这里就先来查看他们对外所提供了哪些接口。首先观察一下两个基类接口BasePresenter和基本视图。
BasePresenter
包com。举例。安卓。建筑。蓝图。todo app
公共接口BasePresenter {
void start();
}
在BasePresenter中只存在一个开始方法。这个方法一般所执行的任务是在提出者中从模型层获取数据,并调用视角接口显示。这个方法一般是在碎片中的简历上方法中调用。
BaseView
包com。举例。安卓。建筑。蓝图。todo app
公共接口BaseViewT {
void set presenter(T presenter);
}
在基本视图中只有一个setPresenter方法,对于视角层会存在一个提出者对象。而setPresenter正是对视角中的提出者进行初始化。
AddEditTaskContract
在机器人官方给出的最有价值球员架构当中对于提出者接口和视角接口提供的形式与我们平时在网上所见的有所不同。在这里将提出者中的接口和视角的接口都放在了AddEditTaskContract类里面。这样一来我们能够更清晰的看到在提出者层和视角层中有哪些功能,方便我们以后的维护。下面就来看一下这个AddEditTaskContract类。
公共接口AddEditTaskContract {
界面视图扩展了BaseViewPresenter {
void showEmptyTaskError();
void showTasksList();
无效设置标题(字符串标题);
空集合描述(字符串描述);
布尔是active();
}
界面演示者扩展BasePresenter {
void createTask(字符串标题,字符串描述);
void更新任务(字符串标题,字符串描述);
void填充任务();
}
}
在这里很清晰的可以看出在视角层中处理了一些数据显示的操作,而在提出者层中则是对工作保存,更新等操作。
Presenter层的实现
下面就来看一下在提出者是如何实现的。
公共类AddEditTaskPresenter实现AddEditTaskContract .演示者,
任务数据源.GetTaskCallback {
.
public addedittaskspresenter(@可空字符串taskId,@非空任务数据源任务存储库,
@NonNull AddEditTaskContract .视图添加任务视图){
mTaskId=taskId
mTasksRepository=checkNotNull(任务存储库);
mAddTaskView=checkNotNull(addTaskView);
maddtaskview。设置演示者(this);
}
@覆盖
public void start() {
if (mTaskId!=null) {
填充任务();
}
}
.
@覆盖
public void populateTask() {
if (mTaskId==null) {
引发新的运行时异常('填充任务()已被调用,但任务是新的。);
}
mtasksrepository。获取任务(mTaskId,this);
}
@覆盖
公共void onTaskLoaded(任务任务){
//视图可能无法再处理用户界面更新
if (mAddTaskView.isActive()) {
maddtaskview。设置标题(任务。gettitle());
maddtaskview。设置描述(任务。获取描述());
}
}
.
}
在这里可以看到在AddEditTaskPresenter中它不仅实现了自己的提出者接口,也实现了GetTaskCallback的回调接口。并且在提出者中包含了模型层任务数据源的对象任务仓库和视角层AddEditTaskContract .视角的对象mAddTaskView。于是整个业务逻辑的处理就担负在提出者的身上。
从Presenter的业务处理可以看出,首先调用模型层的接口getTask方法,通过TaskId查询任务。任务查询完成后,模型层的回调接口GetTaskCallback在呈现层实现。此时在Presenter层,通过onTaskLoaded方法获取任务对象,最后通过调用View层接口实现数据展示。
View层的实现
视图在Fragment中实现,而在Activity中完成Fragment的添加和Presenter的创建。让我们先来看看AddEditTaskActivity类。
公共类AddEditTaskActivity扩展了AppCompatActivity {
@覆盖
受保护的void onCreate(Bundle saved instancestate){
super . oncreate(savedInstanceState);
set content view(r . layout . add task _ act);
.
if (addEditTaskFragment==null) {
addEditTaskFragment=addEditTaskFragment . new instance();
.
activity utils . addfragmenttoactivity(getSupportFragmentManager(),
addEditTaskFragment,r . id . content frame);
}
//创建演示者
新的AddEditTaskPresenter(
taskId,
injection . providetasksrepository(getApplicationContext()),
addEditTaskFragment);
}
.
}
Activity提供的功能也很简单。首先,创建一个片段对象,并将其添加到Activity中。之后,创建Presenter对象,并将片段或视图传递给Presenter。
我们来看看View的实现,也就是Fragment。
公共类AddEditTaskFragment扩展片段实现AddEditTaskContract。视图{
.
@覆盖
公共void onResume() {
super . on resume();
mpresenter . start();
}
@覆盖
public void set presenter(@ NonNull AddEditTaskContract。演示者演示者){
mPresenter=checkNotNull(演示者);
}
.
@覆盖
公共void setTitle(字符串标题){
mTitle.setText(标题);
}
.
}
现在,我不发布太多关于源代码的内容。在Fragment中,通过setPresenter获取Presenter对象。并通过调用Presenter中的方法实现业务处理。然而在Fragment中,它只是UI上的一些操作。这样一来,片段类型的代码减少了很多,逻辑也更清晰了。
我们注意到视图层的实现是通过Fragment完成的。为什么视图的实现要采用片段而不是活动?我们来看看官方的解释。
活动和片段之间的分离非常适合MVP的实现:活动是创建和连接视图和呈现者的总控制器。
多视图的平板电脑布局或屏幕利用了Fragments框架。
在此,官方对采用碎片的原因给出了两种解释。
活动和片段的分离非常适合MVP架构的实现。这里,Activity被用作全局控制器来链接Presenter和View。
碎片更有利于平板或多视图屏幕的布局。
总结
通过MVP架构的使用,可以看出不同层级之间的职责更加单一清晰,同时大大降低了代码的耦合度。对于官方的MVP架构示例,google也明确表示,他们给出的架构示例只是参考,并不是标准。因此,基础MVP架构的扩展空间更大。比如综合谷歌给出的例子。我们可以通过使用dagger2、rxJava等构建一个干净的架构。基于MVP架构。也是个不错的选择。
这就是本文的全部内容。希望对大家的学习有帮助,支持我们。
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。