MVI架构中的单个时间事件

时间:2017-12-12 16:50:48

标签: android architecture kotlin mosby

尝试新的体系结构范例,其中presenter创建不可变状态(模型)流和视图只是呈现它。

无法理解如何处理我们只需要一次制作某个事件的情况。有几个例子。

1)笔记应用程序。我们有editTextsaveButton。用户点击saveButton,发生了一些处理,应该清除editText。你们能否描述我们ViewState这里的内容和近似逻辑流程?

我现在看到的问题和陷阱:

  1. 我们在演示者中订阅了editText.textChanges()。如果我们在text中有ViewState并为每个渲染调用渲染它,那么我们将进入递归,因为它将发出新的textChange并将更新状态并再次渲染。
  2. 我们是否需要text中的ViewState才能在方向文本或进程终止/恢复时恢复它,看起来它在这里开箱即用。但想象一下recyclerView的滚动位置。我们肯定需要保存它才能恢复。我们无法在每次渲染调用时恢复它,因为它看起来很奇怪,不是吗?
  3. 如果我们认为这样的逻辑是副作用并且会调用.doOnNext{ view.clearText() }它是有道理的,但我们是否参考了规范MVI实现中的视图?正如我所见,莫斯比没有它。
  4. 这是有道理的,但在doOnNext电话的那一刻,观点有可能死亡。 MVI应该帮助我们解决这个问题,但前提是它只是ViewState的一部分,对吧?
  5. 2)Github app。第一个屏幕(组织):orgEditTextokButtonprogressBar。第二个屏幕(Repos):recyclerView。当用户将组织输入orgEditText并点击okButton应用时,应向API发出请求,并在成功时导航到Repos屏幕成功或在失败时显示吐司。再次请你为Org屏幕描述ViewState以及应该是什么样的逻辑?

    我现在看到的问题和陷阱:

    1. 我们应该在加载时显示progressBar并禁用okButton。我们应该喜欢加载/内容/错误密封类(让我们称之为ContentState)并将其实例放在我们的ViewState中。 View了解如何呈现ContentState.loading并显示progressBar并禁用okButton。我是对的吗?
    2. 如何处理导航呢?与1.3和1.4相同的问题。
    3. 我已经看到导航应被视为副作用的意见,但同样是1.4。
    4. 吐司 - 有什么州或我们认为这是副作用?同样的问题。
    5. Google建议使用SingleLiveEvent解决方案,但它看起来很奇怪,然后我们应该有尽可能多的LiveData<SingleLiveEvent>个流,而不是真正的单一来源。 其他人建议使用render func生成的新意图更好,但有些异步操作可能会再次改变状态,我们将在第一次显示时获得第二个Toast,依此类推。

1 个答案:

答案 0 :(得分:5)

1)笔记应用程序: 在一个完美的世界中:是的,只要用户插入文本并呈现,您的ViewState就会发生text更改。关于递归:我可能错了,但我认为RxBindings提供了一个Observable,它不仅包含已更改的文本,还包含布尔标志,如果此更改是由用户输入或通过编程设置文本引起的。无论如何,我认为如果你检查if (editText.text != viewState.text)并且仅在文本不同的情况下设置文本,我也可以解决递归问题(请记住,您可能必须使用后续文本被更改时触发的TextWatcher回调启动意图,而不是“在将要改变之前”。)

据说,在Android上我们并不是生活在一个完美的世界。正如您已经说过的,文本将由android自动恢复。因此,不将文本作为ViewState的一部分是有意义的。

所以听起来在这种情况下,ViewState只是一个这样的枚举:

enum ViewState {
   // The user can type typing text
   IDLING,

   // The app is saving the note
   PROCESSING,

   // After having saved (PROCESSING) the note, CLEARED means, show a new empty note  
   CLEARED
}

所以初始状态是IDLING。然后,一旦保存了音符,下一个发出的ViewState就是PROCESSING。一旦此操作成功,您的业务逻辑会立即触发CLEARED,然后立即触发IDLING,最后用户再次看到一个空注,然后可以开始输入新笔记。

请勿使用doOnNext()来操纵视图。 ViewState是视图的唯一真实来源。

关于RecyclerView:RecyclerView会自动恢复它的滚动位置,否则(在恢复状态后将LayoutManager和/或适配器设置为迟到)。不过,如果你想在ViewState中对滚动位置进行建模,那么在完美的世界中,我认为这是最好的解决方案,你应该考虑不在每个滚动的像素上更新ViewState中的滚动位置,而是在用户做一次不再滚动/ fling已经完成。

2)Github app:

  
      
  1. 我们应该在加载时显示progressBar并禁用okButton。我们   应该像加载/内容/错误密封类(让我们称之为   ContentState)并在ViewState中拥有它的实例。查看知道如何   呈现ContentState.loading并显示progressBar和disables   okButton。我是对的吗?
  2.   

  
      
  1. 如何处理导航呢?
  2.   

对我而言,将此作为副作用处理效果很好:我有一个类Navigator,它被注入到演示者中并在doOnNext { navigator.goToX() }中使用。然后导航器将其分派给另一个可以临时连接/分离的组件。所以这个其他组件正在观察导航器中的“导航事件”我之所以这样做,那么这个组件就不会泄漏活动/片段上下文。 “这个组件”可以直接是Activity或Fragment或者其他什么,但我倾向于有一个专门的类,让我们称之为Router,观察导航事件的Navigator并执行FragmentTransactions或者您在应用中使用的任何内容。

  
      
  1. 吐司 - 有什么州或我们认为这是副作用?同样的问题。
  2.   

可以使用与Snackbar相同的方法处理此问题(请参阅here)。 Toast没有隐藏Toast的API。因此,您可以立即触发两个ViewState而不是计时器:第一个设置了错误标志(然后导致Toast显示在屏幕上),第二个设置“清除”此标志。像这样:

Observable.just( ViewState(error = true, ...), new ViewState( error = false, ... )

我希望澄清一些事情,但一如既往:不要把它们当成银子弹。做任何最适合您的应用和用例的事情。不要超级宗教,这总是一个案例决定。