1

RxJava (2.0) で何かを実装する方法に頭を悩ませようとしています。これは Android 用であり、私は Kotlin を使用していますが、ここではプラットフォームと言語の選択は重要ではありません。

アイデアは、RxJava に基づいてある種の MVP アーキテクチャを構築することです。この実装では、ライフサイクル イベント、またはビューがアタッチされているかデタッチされているかを示す値のストリーム (簡単にするために s ) を公開する ( もカスタムActivityも可能性があります) について考えています。FragmentViewBoolean

根底にある考え方は基本的に次のとおりです。

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 つのストリームをマージする必要がありましたが、それcombineLatestzipうまくいかなかったので、トリックに頼って、両方の異なるタイプのイベントに共通のラッパーを作成しました。再び特定のオーバーヘッドが発生します。

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>()
            } }
}
4

2 に答える 2