个人博客:CODE FRAMER BIGZ
MVP系列文章配套DEMO
Android 当中的 MVP 模式(一)基本概念
Android 当中的 MVP 模式(二)封装
Android 当中的 MVP 模式(三)基于分页列表的封装
Android 当中的 MVP 模式(四)插曲-封装 OkHttp
Android 当中的 MVP 模式(五)封装之后的 OkHttp 工具在 Model 层的使用
Android 当中的 MVP 模式(六)View 层 Activity 的基类— BaseMvpActivity 的封装
Android 当中的 MVP 模式(七)终篇—关于对 MVP 模式中代码臃肿问题的思考
摘要:在Android当中的MVP模式(一)基本概念中,用了一个简单的的登录Demo展示了一下 MVP
模式的基本姿势,虽然项目结构是更加清晰了,但是代码量明显增多了,原来的网络请求操作只用 1
个类可以搞定,现在需要 4
个类,并且每当有不同作用的 model
出现时,我们就需要相应的为他们添加 presenter
层的对象,但是细细查看,这些model
的作用都大体相似,与获取数据相关,类似于网络请求或者是数据库 DAO
的操作,所以此处可以考虑将他们的共性抽取出来,封装成基累,然后子类去继承即可。
一个简单的需求:通过
url
获取数据,然后用Gson
解析成JavaBean
,然后展示到 ListView上。这里使用知乎日报的获取最新消息的API
接口 https://news-at.zhihu.com/api/4/news/latest
那么按照普通 MVP
的思路,首先 view
层:
为了突出重点,当前View层只做一件事情:就是展示获取的数据
ILatestVIew
此接口需要一个 String
类型的列表数据,主要是用于给 Adapter
展示用。
LatestViewActivity
很简答,就是实现接口。
IRequestLatestModel
请求服务器端数据的接口
RequestLatestNewsModel
使用 okhttp
请求数据,然后将返回的json类型数据传递给 Presenter
层。
ILatestNewsPresenter
一个接口用于处理 Json
数据,一个接口用于通知 model
层向服务器发起请求 。
LatestNewsPresenter
实现接口定义的方法
其中 HttpUtils
方法如下:
此处
OKhttp
也可以进行封装, 后面再写一篇文章, 专门记录,先暂时简单的使用。
运行之后,点击 button
, 即可发起网络请求,运行效果如下:
弊端:
假设我们现在又有另外的一个需求, 请求知乎日报过往的消息, 对应的 API
接口为URL: https://news-at.zhihu.com/api/4/news/before/20131119
,那么我就需要按照上述的方式,又写一套MVP的代码,最少又得留个类,如此一来,随着需求的增多,代码量会极具增大,但是多余增加的每层代码所做的事情又大多数相同,只是具体细节不一样,那么我们可不可以把每一层要做的事情给抽取出来,封装成基类,然后让子类去继承,去实现,这样就可以大量减少代码量? 抱着这个问题,我就来分析一下 MVP
每一层所做的事情。
以上面请求知乎日报的最新消息为例,分析每一层的职责。
Model
层Model
角色主要是提供数据的存取功,并且将数据或者是错误信息回调给 Presenter
层。更直白的说,Model
就是封装了数据库 DAO
或者网络获取数据的角色,或者两种数据获取方式的集合。所以它主要的功能是:
1. 向数据源发起请求
2. 取消发起的请求
3. 通知 Presenter 处理结果
Presenter
层一般是通知 Model
向服务器发起请求,然后接收 Model
层的请求结果,包括成功的数据和错误的信息,同时也负责将处理之后的数据或者是错误信息通知 View
层,由 View
层作展示。所以他的主要功能是:
1. 通知 Model 层向服务器发起数据请求
2. 通知 Model 层取消这次请求
3. 接收 Model 层返回的数据
4. 接收 Model 层返回的错误信息
5. 通知 View 层接收处理之后的结果或者是错误信息
View
层此处 View 层的作用就比较专一化,只用于处理 UI
相关的事情,不再负责业务逻辑。主要职责如下:
1. Loading 状态的展示隐藏
2. 接收 Presenter 层处理后的数据
3. 接收 Presenter 层处理后的错误信息
4. 接收 Presenter 层处理后的服务器拒绝信息
嗯,差不多就是这么多吧
既然将每一层的主要职责总结了出来, 很明显就可以将这些职责「在代码中就是对应的方法」抽象成方法,然后让子类去个性化的实现。
Model
层IBaseModel
其中 setMethod
和 setRequestUrl
方法直接在 Presenter
的构造方法中调用,设置好请求的方式和请求的 Url
地址,这样方便 model
层在请求服务器数据时,使用对应的参数,使用对应的请求方式。
此处没有用到
method
是因为知乎日报的最新新闻 API 接口是 Get 方式,不需要参数,所以此处没有根据请求方式来调用不同的请求方法
Presenter
层IBasePresenter
Presenter
层是逻辑控制层,是 Model
层和 View
层的桥梁,对这一层抽取共性进行封装的时候,不能像 Model
层一样,把全部的功能装好好,原因如下:
1.如果将其全部封装起来,是没办法复用同一个功能模块的,并且会导致部分业务逻辑需要在 view 层中做处理,这样和 MVP 的思想相悖。
2.Presenter 层需要处理和 View 层的交互逻辑以及 Model 层返回的数据。
但是 Model
层是可以的,我是认为,Model
层就是从数据源中拿数据,并且将数据传递给 Presenter
层,所有的 Model
层做的都是这个操作,只是访问数据源的参数不同,数据源类型不同,访问数据源的方法不同而已,所以很明显可以全部抽取出来放基类中,然后各个子类去各自实现。
1. requestServer 在View层调用的接口,用于通知Model层想服务器发起请求,参数可为空,比如,有些Get方式的请求就不需要参数
2. requestSuccess 在Model层调用,通过此方法将服务器返回的数据传递给给Presenter层处理
3. cancelRequest 在View层调用,用于通知Model层取消请求
4. okHttpError 在Model层调用,当网络请求产生错误的时候
5. getModel 在子类中调用,用于拿到Model对象
6. getParams 在Model层中调用,此方法用于获取Presenter层处理好的参数
BasePresenter
public abstract class BasePresenter implements IBasePresenter
这是一个泛型的抽象类,其中泛型Params
是用于model
层向服务器发起请求的请求参数,Data
是服务器返回的Json
类对应的JavaBean
类。BasePresenter
处理了View
层和model
层中大多数的逻辑,我们要做的就是在子类中实现public abstract void serverResponse(Data data);
这个抽象方法就好了。public abstract void serverResponse(Data data);
这个方法是在用于处理model
层返回的结果,然后进行处理之后回调给view
层。46、47、50、51、52
行的代码给注释掉了,其实一般情况下这里是不需要注释的,这里是用于判断返回数据的errorNum errorType errorDesc
信息的,这么操作,是为了实现如下功能:若返回的信息有误,则BasePresenter直接回调给View
层,如果正确,才会传递给子类。 上述最后一条,需要对泛型Data
在进行一次封装,并且使用上Gson
的@SerializedName(value = "...",alternate = {"...","...","..."})
这个注解,并且这里涉及到 泛型擦除的问题,这一块我还没有很好的解决办法,所以此处没有进行封装。
View
层还是按照上面分析的 View
层职责来写:
IBaseView
到此为止,对 MVP
模式的每一层都写出了对应的基类,有了这件基类作为基础之后,在进行同样的网络请求。
LatestNewsModel
LatestNewsPresenter
其中Param
泛型参数填的是nullable
是因为这个请求是get
方式,没有涉及到参数。LatestNews
作为Data
的泛型,主要是用于BasePresenter
解析并映射。
ILatestNewsView
IlatestNewsVIew
接口是继承IBaseView
接口的,是因为它需要在IBaseView
接口所定义的功能之上,还需要实现将数据展示到列表中这么一个操作,所以添加上了一个showLatestViewTitle
方法。
LatestNewsTitleActivity
这个类写起来就简单了,跟着接口来, 把之前每一个接口提到的功能给实现以下就可以了。
顺便贴个 XML
文件:
搞定,实现的效果和上面是一样的。
回过头一看,MMP,这代码量似乎也没有少很多啊,-。- ,没事没事,需求多了就少了~
先看看上一篇中提到的一张图
此处将MVP模式封装后,MVP的流程图如下:
后面的文章将使用上面封装的框架,通过扩展 BasePresenter
来增加新的模块。
个人博客地址 :CODER FRAMER BIGZ