我正在努力思考如何在 RxJava (2.0) 中实现某些东西。它适用于 Android,我使用的是 Kotlin,尽管平台和语言的选择在这里并不重要。
我的想法是我会在 RxJava 上建立某种 MVP 架构。在这个实现中,我正在考虑一个Activity
(也可以是 aFragment
或自定义View
)公开一个值流(Boolean
为简单起见 s ),这些值指示生命周期事件,或者视图是附加还是分离。
基本思想基本上是这样的:
private val lifecycleEvents = PublishSubject.create<Boolean>()
val screenStates: Observable<Boolean> = lifecycleEvents.hide()
override fun onResume() {
super.onResume()
lifecycleEvents.onNext(true) // I'm attached!
}
override fun onPause() {
lifecycleEvents.onNext(false) // I'm detached!
super.onPause()
}
override fun onDestroy() {
lifecycleEvents.onComplete() // I'm gone
super.onDestroy()
}
然后从另一端,Presenter 公开一个Observable
表示屏幕状态的对象流 - 由 View 呈现。
(这遵循本系列http://hannesdorfmann.com/android/mosby3-mvi-1中解释的概念- 这归结为这样一个事实,即演示者使用独立对象提供视图,这些对象完全封装了屏幕状态,而不是多个不同的视图上的方法)。
然后我想绑定这两个可观察的流,以便:
每当 View 分离时,来自 Presenter 的输入都会被忽略(并且它没有被缓冲,以免遇到任何背压问题)
然而,一旦 View 被重新连接,它就会获取Presenter 发出的最新状态。换句话说,最多只缓存一个状态实例。
它将按如下方式工作(String
为简单起见,假设状态是类型):
val merged: Observable<String> = ???
val attached = true
val disattached = false
screenStates.onNext(attached)
fromPresenter.onNext("state A")
fromPresenter.onNext("state B")
screenStates.onNext(disattached)
fromPresenter.onNext("state C") // this won't survive at the end
fromPresenter.onNext("state D") // this will "override" the previous one.
// as that's the last state from BEFORE the screen is reattached
screenStates.onNext(attached)
// "state D" should be replayed at this point, "state C" is skipped and lost
fromPresenter.onNext("state E")
// what "merged" is supposed to have received at this point:
// "state A", "state B", "state D", "state E"
我不确定最好的惯用解决方案是什么。
我试图将它实现为ObservableTransformer
,但我不能完全正确。我相信转换器应该是无状态的,而我的解决方案倾向于明确跟踪发出的内容并“手动”缓冲最后一个元素等,这感觉很混乱而且太紧迫了,所以我认为这是错误的。
我找到了https://github.com/akarnokd/RxJava2Extensions/blob/master/src/main/java/hu/akarnokd/rxjava2/operators/FlowableValve.java,但实现看起来非常复杂,我不敢相信它可以'不要以更简单的方式完成(我不需要所有的灵活性,我只想要适用于所描述用例的东西)。
任何见解都将不胜感激,包括在 Android 的背景下是否还有其他我应该考虑的事情。另请注意,我不使用 RxKotlin 绑定(我可能会,我只是不认为这里应该需要它们)。
编辑:
下面是我目前的实现。正如我所说,我对此不太满意,因为它是明确的有状态的,我相信这应该以声明的方式实现,利用 RxJava 的一些结构。
我需要合并两个不同类型的流,因为combineLatest
也zip
没有完全做到这一点,所以我使用了一个技巧,为两种不同类型的事件创建一个通用包装器。它再次引入了一定的开销。
sealed class Event
class StateEvent(val state: String): Event()
class LifecycleEvent(val attached: Boolean): Event()
class ValveTransformer(val valve: Observable<Boolean>) : ObservableTransformer<String, String> {
var lastStateEvent: Event? = null
var lastLifecycleEvent = LifecycleEvent(false)
private fun buffer(event: StateEvent) {
lastStateEvent = event
}
private fun buffer(event: LifecycleEvent) {
lastLifecycleEvent = event
}
private fun popLastState(): String {
val bufferedState = (lastStateEvent as StateEvent).state
lastStateEvent = null
return bufferedState
}
override fun apply(upstream: Observable<String>): ObservableSource<String> = Observable
.merge(
upstream.map(::StateEvent).doOnNext { buffer(it) },
valve.distinctUntilChanged().map(::LifecycleEvent).doOnNext { buffer (it) })
.switchMap { when {
it is LifecycleEvent && it.attached && lastStateEvent != null ->
// the screen is attached now, pump the pending state out of the buffer
just(popLastState())
it is StateEvent && lastLifecycleEvent.attached -> just(it.state)
else -> empty<String>()
} }
}