RxJava (2.0) で何かを実装する方法に頭を悩ませようとしています。これは Android 用であり、私は Kotlin を使用していますが、ここではプラットフォームと言語の選択は重要ではありません。
アイデアは、RxJava に基づいてある種の MVP アーキテクチャを構築することです。この実装では、ライフサイクル イベント、またはビューがアタッチされているかデタッチされているかを示す値のストリーム (簡単にするために s ) を公開する ( もカスタムActivity
も可能性があります) について考えています。Fragment
View
Boolean
根底にある考え方は基本的に次のとおりです。
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()
}
そしてもう一方の端から、プレゼンターはObservable
、ビューによってレンダリングされる、画面の状態を表すオブジェクトのストリームである を公開します。
(これは、このシリーズhttp://hannesdorfmann.com/android/mosby3-mvi-1で説明されている概念に従います。これは、プレゼンターが、複数の異なる状態ではなく、画面の状態全体をカプセル化するスタンドアロン オブジェクトをビューにフィードするという事実に要約されます。ビューのメソッド)。
そして、これら 2 つの監視可能なストリームを次のようにバインドしたいと思います。
ビューがデタッチされるたびに、プレゼンターからの入力は無視されます (バックプレッシャの問題が発生しないように、バッファリングされません)。
ただし、ビューが再アタッチされると、プレゼンターが発行した最新の状態が取得されます。つまり、最大で 1 つの状態インスタンスのみがバッファリングされます。
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のいくつかの構造を利用して、宣言的に実現する必要があると私は信じています.
異なるタイプの 2 つのストリームをマージする必要がありましたが、それ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>()
} }
}