Rx如何创建一个根据请求更新或刷新的列表

时间:2018-01-24 13:39:20

标签: android rx-java2 rx-android rx-kotlin2

我正在开发一个应用程序,显示从互联网上获取的项目列表。 我有2个按钮loadMore并刷新,loadMore - 加载下一批项目,刷新 - 从头开始​​加载项目。

我正在使用MVI(模型视图意图)模式。 为了简单起见,我创建了一个使用数字列表的示例,每个数字代表一批项目:

    val loadSubject = BehaviorSubject.create<Unit>()
    val refreshSubject = PublishSubject.create<Unit>()

    val list = loadSubject.scanWith(
            { Observable.just(emptyList<Int>()) },
            { listObservable, _ ->
                listObservable
                        .map { it + ++count }
                        .replay().autoConnect()
            }
    )
            .flatMap { it }
            .filter { it.isNotEmpty() }


    val listSubscription = {
        list.subscribe {
            //do whatever with the list
        }
    }

    refreshSubject.scanWith(
            listSubscription,
            { disposable, _ ->
                disposable.dispose()
                listSubscription()
            }
    ).subscribe()

所以现在它可以完美地运行,但订阅是在我的Intent中,我需要一个使用Rx的方法,它可以做同样的事情,但让我的View订阅。

我想要的是:

我的列表是[1,2,3]

on loadMore按ill get [1,2,3,4]

刷新时按下病例[5]

2 个答案:

答案 0 :(得分:0)

val loadSubject = PublishSubject.create<Unit>()
val refreshSubject = PublishSubject.create<Unit>()

val loadMoreSequence = defer {
  loadSubject.scanWith(
    { Observable.just(listOf(0)) },
    { listObservable, _ ->
      listObservable
        .map { it + it.size }
        .replay().autoConnect()
    }
  )
    .flatMap { it }
}

val list = concat(Unit.asObservable(), refreshSubject)
  .switchMap { loadMoreSequence }

// testing

list.subscribe {
  println(it)
}

loadSubject.onNext(Unit)
loadSubject.onNext(Unit)
refreshSubject.onNext(Unit)
loadSubject.onNext(Unit)

结果:

[0]
[0, 1]
[0, 1, 2]
[0]
[0, 1]

答案 1 :(得分:0)

我设法得到了一些工作:

    val loadSubject = PublishSubject.create<Unit>()
    val refreshSubject = PublishSubject.create<Unit>()

    val loadList: (ItemsProvider) -> Observable<SingleEvent<List<Int>>> = { provider ->
        val markedGet = Observable.fromCallable(provider::fetchItem)
                .markStartAndEnd()

        loadSubject.concatWith(Unit)
                .flatMapWithDrop(markedGet)
                .publish().autoConnect()
    }

    val listEvents = refreshSubject.concatWith(Unit)
            .switchMap { loadList(itemsProvider) }

    val list = refreshSubject.concatWith(Unit)
            .switchMap {
                listEvents.filter { it is SingleEvent.Result }
                        .map { (it as SingleEvent.Result).data }
                        .scan { list: List<Int>, newList: List<Int> ->
                            list + newList
                        }
            }

    val isListEmpty = list.map { it.isEmpty() }

    val isDownloading = listEvents.map { it.isRunning() }

    val isRefreshing =
            Observable.merge(
                    refreshSubject.map { true },
                    isDownloading.filter { !it }.skip(1)
            )

扩展名为:

fun <T> Observable<T>.concatWith(item: T): Observable<T> =
        Observable.concat(Observable.just(item), this)

sealed class SingleEvent<T> {
    class Start<T> : SingleEvent<T>()
    data class Result<T>(val data: T) : SingleEvent<T>()
}

fun <T> Observable<T>.markStartAndEnd(): Observable<SingleEvent<T>> {
    return this.map { SingleEvent.Result(it) as SingleEvent<T> }
            .startWith(SingleEvent.Start())
}

/**
* Flatmaps upstream items into [source] items.
* Ignores upstream items if there is any [source] instance is currently running.
*
* upstream ----u-----u---u-------u---------------|-->
*              ↓                 ↓               ↓
* source       ---s-------|->    ---s-------|->  ↓
*                 ↓                 ↓            ↓
* result   -------s-----------------s------------|-->
*/
fun <T, R> Observable<T>.flatMapWithDrop(source: Observable<R>): Observable<R> =
        this.toFlowable(BackpressureStrategy.DROP)
                .flatMap({ source.toFlowable(BackpressureStrategy.MISSING) }, 1)
                .toObservable()

基于Dmitry Ryadnenko blog post

但我无法使flatMapWithDrop工作,因为我正在进行网络调用,我需要删除任何传入的刷新请求,直到当前的刷新请求结束。

更新:有没有其他方法可以缓存列表(不再使用refreshSubject.concatWith(Unit).switchMap {})?