Android 架构设计:手把手教你撸一个简洁而强大的 MVP 框架!
override fun onLoginFailed() {...}
// 发起 login 任务请求
fun onLoginClick() {presenter.login(username, password)}
}
真正的View
层。可以是Activity
,?Fragment
等。是真正进行界面更新的地方。
View
层需要持有Presenter
的对象,用于在需要的时候使用presenter
发起具体的数据请求处理任务
,比如上例中点击进行登录时。通过presenter
发起了登录任务, 并通过LoginView
协议接口,接收任务处理回调
进行界面更新
3. LoginApis
LoginApis.login(username, password, object Callback {
override fun onSuccess() { view.onLoginSuccess() }
override fun onFailed() { view.onLoginFailed() }
})
Model
层,也叫数据提供层。
与其他的MVP
不同,这里并没有要求Model
层需要定义一个特殊的接口去进行实现。所有的功能性api
。均可被视作为Model
层。比如这里LoginApis
,便是用于提供登录模块的网络任务入口
。
Model
层与Presenter
层的通信方式主要分为两种:一种直接从 Model 层同步获取
到指定数据,另一种是通过异步回调
的方式获取到指定数据,即此处LoginApis
的通信方式。
请注意避免直接向Model
层传递Presenter
去进行数据获取。保证Model
的独立性
4. LoginPresenter
Presenter
层,连接 V-M 的中间枢纽层。复杂的数据业务逻辑都在这里进行处理。
结合上方示例:一个完整的 M-P-V 通信流程,可分为以下几步:
由
V层向P层
发起具体的处理任务P层
接收到V层
发起的任务。调用M层
的 api,获取到原始数据
P层
对从M层
获取到的原始数据
进行预处理(本示例因为较简单,故没有这一步)。将
处理完毕后
的数据。通过V层
提供的协议接口,通知到V层
去进行界面更新。
MVP 实现
通过上面的实例与讲解。相信可以使大家对 MVP 模型有一定的了解了,下面我们将一步步的介绍整个MVP模型框架的搭建
1. 基础通信协议接口定义:MVPView
interface MVPView {
fun getHostActivity():Activity
fun showLoadingDialog()
fun hideLoadingDialog()
fun toastMessage(message:String)
fun toastMessage(resId:Int)
}
MVPView
中定义了一些基础的协议方法。这些方法是所有V层
都需要的功能。比如Toast展示
、进行异步任务时的加载中Dialog展示
等。
2. 基础 Presenter 创建
open class MVPPresenter<T:MVPView>(private var view:T?){
fun attach(t:T) {
this.view = t
}
fun detach() {
this.view = null
}
fun isViewAttached() = view != null
fun getActivity() = view?.getHostActivity()?:throw RuntimeException("Could not call getActivity if the View is not attached")
// Lifecycle delegate
open fun onCreate(bundle: Bundle?) {}
...
open fun onDestroy(){}
}
我们来一条条的捋一下:
首先。我们在上面的 MVP 说明中说过,MVPView
为协议接口,提供出来用于进行P-V绑定
,所以相应的就会有P-V的绑定与解绑功能
(为了方便使用,这里也让默认构造器自动进行了 P-V 绑定)
fun attach(t:T) { this.view = t }
fun detach() { this.view = null }
然后,我们会经常需要在 P 层中使用绑定的 Activity 去进行各种操作。而p层
是不建议去持有Context实例
的,所以在此提供一个getActivity
方法,从绑定的view
中去获取正确的Activity实例
提供使用:
fun getActivity() = view?.getActivity()?:throw RuntimeException("Could not call getActivity if the View is not attached")
而isViewAttached
方法,则是专门为异步回调任务
所设计的 api。因为如果是使用异步回调的方式
去从 model 层获取的数据。那么很可能,接收到回调消息的之前,这个时候view已被解绑置空
。导致通知失败
所以。一般来说。对于任务中有异步回调操作
的,应该在回调处。先行判断是否已解绑
。若已解绑则跳过当前操作:
if (!isViewAttached()) return
view?.hideLoadingDialog()
最后,则是提供的一堆onXXX
生命周期方法了。用于与 activity/fragment 中的生命周期进行绑定。
3. V-P 连接器
与其他的MVP
相比不同,这里提供MVPDispatcher
作为V-P连接器
:
class MVPDispatcher{
private val presenters:MutableList<MVPPresenter<*>> = mutableListOf()
// ==== 添加与移除 Presenter ========
fun <V:MVPView> addPresenter(presenter:MVPPresenter<V>) {...}
internal fun <V:MVPView> removePresenter(presenter:MVPPresenter<V>) {...}
// ==== 绑定生命周期 ==========
fun dispatchOnCreate(bundle:Bundle?) {...}
...
fun dispatchOnRestoreInstanceState(savedInstanceState: Bundle?) {...}
}
可以看到此连接器干了以下几件事:
addPresenter
与removePresenter
:向容器presenters
中添加或移除presenter实例
?。?做到对单页面绑定多个 presenter 的效果。dispatchOnXXX
:通过已添加的presenter
进行生命周期通知
.?做到 V-P 生命周期绑定的效果在接收到 V 层
destroy
销毁通知时,自动移除解绑所有的presenter实例
。
fun dispatchOnDestroy() {
presenters.forEach {
if (it.isViewAttached()) { it.onDestroy() }
// 生命方法派发完毕后。自动解绑
removePresenter(it)
}
}
4. BaseMVPActivity 的创建
最后就是真正的V层
的创建了:Activity/Fragment
的 MVP 基类搭建!
这里使用BaseMVPActivity
作为举例说明。fragment
的基类搭建与此大同小异。
首先,我们需要确定BaseMVPActivity
所需要尽到的职责:
1. 一个具体的 V 层,需要持有一个唯一的 MVPDispatcher 进行操作
abstract class BaseMVPActivity:Activity() {
val mvpDispatcher = MVPDispatc
her()
}
2. 统一实现默认协议方法:MVPView
abstract class BaseMVPActivity:Activity(), MVPView {
...
override fun getHostActivity():Activity {...}
override fun showLoadingDialog() {...}
override fun hideLoadingDialog() {...}
override fun toastMessage(message:String) {...}
override fun toastMessage(resId:Int) {...}
}
3. 借助 MVPDispatcher 实现 V-P,一对多的绑定
abstract class BaseMVPActivity:Activity(), MVPView {
...
// 由子类提供当前页面所有需要绑定的 Presenter。
open fun createPresenters():Array<out MVPPresenter<*>>? = null
override fun onCreate(savedInstanceState: Bundle?) {
...
// 创建所有的 presenter 实例,并通过 mvpDispatcher 进行绑定
createPresenters()?.forEach { mvpDispatcher.addPresenter(it) }
}
}
比如在最上面我们所举例的 LoginActivity。假设现在我们需要对登录页再添加一个验证码校验逻辑
. 此逻辑被放在了CaptchaPresenter
中:
class LoginActivity:BaseMVPActivity(),LoginView, CaptchaView {
// 登录的 Presenter 实现
val loginPresenter = LoginPresenter(this)
// 验证码的 Presenter 实现
val captchaPresenter = CaptchaPresenter(this)
// 绑定多个 Presenter
override fun createPresenters() = arrayOf(loginPresenter, captchaPresenter)
...
}
这就做到了 Presenter 的复用。在需要共用一些基础业务逻辑
的时候。此一对多的绑定
是个很好的特性!
4. 借助 MVPDispatcher 实现 V-P 生命周期关联管理
abstract class BaseMVPActivity:Activity(), MVPView {
...// other codes
override fun onCreate(savedInstanceState: Bundle?) {
...
mvpDispatcher.dispatchOnCreate(intent?.extras)
}
...
评论