首页 > 系统 > Android > 正文

Android 项目框架 使用MVP开发

2019-11-07 22:52:08
字体:
来源:转载
供稿:网友

前言

在Android中使用 MVP 来开发已经出来很久了,刚好Google又出了一系列的architecture samples,在此就整理一下对于MVP的认知和实践总结,这篇文章会随着使用经验的丰富而不断更新。

1. 介绍MVC

在没有使用MVP开发之前,我们一直使用的都是MVC模式,其实也不算的MVC,一般我们听到的都是Android中的Activity既是View,又是Controller,即Activity既负责View的显示,又负责处理业务逻辑,这是我们一般听到的,但其实我们的Activity还负责了数据的保存和读取。

当业务逻辑简单,仅仅只是获取数据、展示数据时,我们的代码是这样: Model-View

但其实考虑数据的存储、业务逻辑等等,我们是这样: God_Object 所有东西都连在一起,太复杂以至于成了一个面了,这个面就是这个God Object:Activity。

想想看,我们是不是把所有东西都写在这个上帝类中了:网络请求、解析Json、处理数据、展示数据、缓存数据等等,全部都在Activity中,尽管有些东西已经封装的很好,但依旧都和Activity扯在一起,基本上所有代码都在Activity中,于是所谓的MVC,就叫做 massive view controller,超级大的ViewController。 这样做尽管有好处,很容易看清楚业务逻辑,而且写起来很方便、顺手,不知不觉就这样写了。但是很明显,随着需求不断加多,该类代码就越写越多,并且如果需求有改动的话,那改起来很费劲,而且这样写,不敢轻易重构,因为太复杂,动一点可能就会有bug。

那么不想这么做怎么办呢?于是就有了MVP。

2. 介绍MVP

典型的MVP就像这样: MVP

很明显:

MVP中PResenter和View是相互交互的。MVP拒绝View和Model直接交互,View和Model只能通过Presenter间接交互。这点是合理的,因为Android对UI的操作必须在 UIThread 上,而对Model的处理大多是异步任务。通过转接一层,可以很方便处理线程问题。Model和View是被动的,一切都由Presenter来主导

我们一般把 Activity/Fragment 当做View,那么这时我们的Activity/Fragment中的代码简单多了,响应Presenter的指令对View进行操作,而这些操作是在IVIEW 接口中定义好的。这样大部分代码转移到了Presenter中,而且一些业务代码被封装到了Model层去,这样的代码就会清晰很多,写完一看,哇塞,太清晰了。而且很容易定位bug,毕竟代码职责越清晰越不容易出错。

那么现在分开来介绍:

Model 为UI层提供的数据,或者保存UI层传下来的数据。 Model 是用户界面需要显示数据的抽象,也可以理解为从业务数据(结果)那里到用户界面的抽象。 Model 应该封装业务逻辑,不让Presenter和View知道业务逻辑层View View is a layer that displays data and reacts to user actions. View提供友好的交互、展示数据、响应用户操作,并都转发给Presenter来做具体的处理Presenter 逻辑控制层,分发逻辑。处理着程序各种逻辑的分发,收到View层UI上的反馈命令、定时命令、系统命令等指令后分发处理逻辑交由业务层做具体的业务操作,然后将得到的 Model 给 View 显示。

到这里,关于MVP的基本概念讲解完了,就是分三层写代码,此处建议使用MVP时分包时按照模块分包,模块比较多时,建一个module包,装所有模块,每个模块再细分三层,这样包很清晰、代码也很清晰。

接下来看一些google的示例。

3. Google samples中的MVP

Google samples中的MVP(google MVP示例中这张图真的很完美的阐述了一个简单的app的架构): google samples mvp

Model层获取数据时,使用DataRepository 分发管理,从Presenter处接收指令从RemoteDataSource 或者 LocalDataSource 中获取数据,并回调给Presenter,Presenter再回调给View用于展示。

下面列出一些官方推荐 的点,这些都可以在代码中找到:

Use Fragments as View. 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,which means the Activity is responsible for the creation of fragments and presenters. 使用Fragment作为View,Activity作为全局的Controller把presenter和view绑定起来。

Create a contract interface defining the view and the presenter,which contains the View interface and the presenter interface. Contracts are interfaces used to define the connection between views and presenters. This is a amazing concept. 创建 IContract 父类接口来管理同一个模块下的 IViewipresenter接口,这样很清楚的能看清楚两者的逻辑。

In general, the business logic lives in the presenter and relies on the view to do the Android UI work. 业务逻辑全部在Presenter层,依靠View层来触发。

The view contains almost no logic: it converts the presenter’s commands to UI actions and listens to user actions, which are passed to the presenter. View几乎没有任何逻辑,响应Presenter层的指令,并接收用户操作传给Prenster。当然很少很轻量的逻辑应该在这一层中处理(判空处理等),这一点应该要自己衡量。View应该是被动的。

4. MVP是一个方法论

我们应该很容易看到在MVP中仅仅有一些基本原则,并没有一个固定的方式去实现MVP,都针对自家的业务需求、逻辑去更好的更合适的使用MVP,只要View和Model分离,代码清晰,易于调试,易于测试,都OK。

MVP的一些指导性原则来约束实现:

Model与View不能直接通信,只能通过PresenterPresenter类似于中间人的角色进行协调和调度Model和View是接口,Presenter持有的是一个Model接口和一个View接口Model和View都应该是被动的,一切都由Presenter来主导Model应该把与业务逻辑层的交互封装掉,换句话说Presenter和View不应该知道业务逻辑层View的逻辑应该尽可能的简单,不应该有状态。当事件发生时,调用Presenter来处理,并且不传参数,Presenter处理时再调用View的方法来获取。

知道了MVP是一个方法论,下面说说MVP的另一个实现方式。

5. MVP的另一种实现方式

MVP另一个实现方式(变种)就是不像一般的把Activity/Fragment作为View,而是把他们作为Presenter来使用,单独把View用一个类来管理。

使用该种方式可以大大减少类的个数,因为是依靠 泛型 来解耦的,所以可以不用提供接口,当然提供了也行,我在实现过程中删除了接口,同时把点击事件的响应结果当成逻辑,放在Presenter中,这样就不用传来传去的。(之前使用Fragment作为View时,点击ListItem 需要修改已读状态,传入到Presenter中修改,Presenter修改完后又调用View.showDetailUI()方法又传回到View中,使用变种后这块就大大减轻工作量了)

使用这种方式个人认为有点怪异,一是使用泛型加大了理解难度,写着写着就不知道怎么调用到这块了;二是删除接口后感觉复用性降低了,逻辑也不能复用、View也不能复用,当然可以不删接口,但是不删接口和原来的MVP有什么区别呢。恩,不过这点可能随着使用MVP越深入会越能理解,Activity到底是View还是控制器,可能随着项目的偏重不同,会适合不同的情况吧。

那么说完了MVP变种,下面总结一下个人使用MVP的心得。

6. MVP心得体会

使用MVP开发,顿时感觉清爽很多,虽然多了很多代码,但是瑕不掩瑜,让代码清爽很多。真的很棒,看了重构后的代码,感觉自己之前真是活在噩梦里一样,终于不用把所有代码写在Activity里或是Fragment里了。下面就具体说说使用中的心得体会。

首先是方法论上:

首先分清除什么是 分发业务逻辑、业务逻辑、视图逻辑。 理解这三点对于设计接口、使用MVP写项目、重构等有很大的帮助。视图逻辑可以更好的写View,分发业务逻辑用于Presenter,业务逻辑用于Model,当然一些业务逻辑是在Presenter中的。

然后看什么是View,什么是逻辑,哪些逻辑展示哪些View,哪些操作调用哪些逻辑,这会很好的帮助我们理清代码逻辑、方便相互调用。

一个 View 可以有多个 Presenter,要用到什么业务就加入什么 Presenter,并且实现这个 Presenter 所需要的 View 接口即可,这就是简单的复用逻辑,即Presenter是可复用的。

然后是代码上:

IView接口里的方法全部是UI相关,show()、hide()等,不应该出现逻辑相关。IPresenter接口里的方法应该全部是逻辑相关,进行分发,很明显,要持有Model引用,方便对数据进行操作。不应该出现与Model相关的方法,比如LoadFromLocal,LoadFromRemote,这应该是DataRepository处理的事情。Presenter里面只需要告诉Model说我需要这个数据,即LoadData,而不用管从哪Load数据。这里面只需要负责 分发!!!Model中,对数据进行处理,所有的数据从这里进出,并对数据进行处理。一般创建DataRepository类对LocalDataSource,RemoteDataSource进行管理分发,这三个类都实现IDataSource接口,保证逻辑一致。

最后一些个人体会:

在MVP里面,越来越感到依赖注入的重要性。全部是依赖,全部需要注入,感觉依赖注入框架 Dagger2 是时候展现它强大的功能了。

回调太多。获取数据时,从Presenter中开始callback,再到DataRepository中,再到具体的LocalDataSource或RemoteDataSource的callback,总共回调了三层,增长了链条。很明显,解决这个问题需要用到 Rxjava 了,可以省去很多代码,变得简洁是必然的。

Google samples 中的 IContract 接口管理类真的非常棒,管理了IPresenter和IView,如果可以的话,应该让它也管理IDataSource 这个接口,非常清晰的一种方式。

尽管MVP实现了一定的分离,但相当于大量代码从Activity中转移到Presenter和Model中,尽管使用接口使代码清晰很多,但避免不了难看和难以查找,随着业务的不断复杂,代码依旧会越来越多。所以使用clean架构应该是最好。

最后不得不说确实方法数、类都增加了好多。但这也是必然的。解耦、方便测试。

7. 代码实现

说了那么说,show me the code.

那么,自己写的代码全在GitHub上,全部以分支形式展现:

最基础的分支,全部代码在Activity中: branch_basic

MVP分支,使用MVP重构代码:branch_mvp

MVP另一种实现方式(变种)分支:branch_mvp_variant

之后,肯定会尝试clean架构再次重构。不过由于项目本身较小,所以当个练手的真的不错。

参考:

介绍MVP,非常好的文章 Android MVP模式 简单易懂的介绍方式 Android中的MVP Introduction to Model View Presenter on Android

MVP变种: 用MVP架构开发Android应用 Android MVP — An Alternate Approach

MVP方法论,具有哲学意味的一篇文章,看完后会觉得原来代码还能这么哲学: 说说Android的MVP模式


发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表