状态管理State Manage到MVI—Model-View-Intent上

什么是状态?界面上展示给用户的都是一种状态,如loading显示,error信息显示,列表展示等。这是日常开发中必然会遇到的,本文将讲解如何用更有效的方式来进行状态管理,提高代码的可读性,可维护性,健壮性。。。。。。文章中代码示例比较多,但是别慌,逻辑都比较简单,稳住就行。文章代码使用kotlin实现,关于状态管理部分示例代码使用MVP + RxJava模式来编写。

关于状态管理

假设我们有这样一个需求:在输入框输入用户名,点击保存按钮把用户保存到数据库。在保存数据库之前,显示loading状态,然后把保存按钮设置为不可点击,保存数据库需要异步操作,最后在成功的时候隐藏loading状态并且把保存按钮设置为可点击,若发生错误,需要隐藏loading状态,把保存按钮设置为可点击状态,然后显示错误信息。Show you the code:

这段代码看上去不怎么优雅,但已经实现了我们的需求了。简单画下流程图:

可以看到当保存数据库操作前调用view.showLoading()和view.setButtonUnable(),当操作成功或者错误的时候调用view.hideLoading()和view.setButtonEnable(),像这种“配套”方法越来越多的时候就很容易会疏忽,出现忘记隐藏loading状态,忘记把按钮设置为可点击等问题。在这简单例子你可能会觉得没什么,实际开发的时候一定会记得调用相应的方法,这不同于注册接口监听,一般我们会在Activity#onCreate()的时候注册监听,在Activity#onDestroy()取消监听,但我们在View里可以有很多地方调用Presenter的方法,如setUser(),我们认为调用Presenter方法是一种输入,同时Presenter也有很多地方输出状态给View,如view.showLoading(),view.showError()等。我们不能确定setUser()方法在哪里被调用,view.showLoading()方法在哪里被调用,假设我们还有其他方法在同时执行:

这很容易会造成状态混乱,例如loading状态和错误信息同时出现,当错误信息显示的时候保存按钮没有恢复可点击状态等,在实际的业务中,这种问题尤其明显。

响应式状态(Reative State)

我们能不能限制Presenter只有一个输入,状态只从一个地方输出呢?我们借助PublishSubject作为桥接(如上面代码片段setUserSubject),然后通过Observable.merge()把它们合并成一个流,来实现只有一个地方输入。下面我们主要看看我们如何实现状态只从一个地方输出。

引用面向对象编程一句经典的话:万物皆对象。用户输入用户名,点击保存按钮,这是一个事件,我们把它看成一个事件对象SetUserEvent,把UI状态作为一个状态对象(SetUserState),同时状态是对界面的描述。于是我们在把事件作为输入(SetUserEvent),输出状态(SetUserState),View只需要根据状态SetUserState的信息(如loading,显示错误信息)来展示界面就可以了:

可以看到这是一条单向的“流”,而且是循环的,View把用户事件输出到Presenter,接收状态展示界面;Presenter对View的事件输入进行处理,输出状态。接下来看看如何用代码实现。

首先定义界面状态SetUserState:

这里定义了3个方法,用于表示正在加载状态,成功状态和失败状态。接下来对保存数据库操作进行重写:

修改的核心部分是flatMap里的内部Observable:

一般情况下都会有很多输入,如上拉加载下一页,下拉刷新等。现假设需要添加一个checkUser()方法,用于查询用户是否存在,要把不同输入合并,我们需要定义一个公共的父类UIEvent,让每个输入都继承该父类:

下面是Presenter的实现:

因为篇幅过长,所以小编就分两次写出了哦,希望大家理解!

  • 发表于:
  • 原文链接http://kuaibao.qq.com/s/20180123A0898C00?refer=cp_1026
  • 腾讯「云+社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 yunjia_community@tencent.com 删除。

扫码关注云+社区

领取腾讯云代金券