求助:如何在响应式流中将Single<List<Maybe<Book>>>转为Single<List<Book>>
Single<List<Maybe<Book>>>转Single<List<Book>>的非阻塞方案 你的问题核心是要把嵌套的Maybe类型在流内部处理掉,完全不需要用blockingGet这种阻塞操作——RxJava提供了一套操作符可以优雅地解决这个问题。
先看问题出在哪:原来的getBooks里,it.map(::collectonToBook)是把每个Collection同步转换成Maybe<Book>,所以最终得到的是List<Maybe<Book>>。我们需要把这些Maybe的异步结果合并成一个干净的List<Book>。
最优解决方案
修改getBooks函数,利用RxJava的流操作符拆解列表、处理Maybe并收集结果:
fun getBooks(): Single<List<Book>> { return getCollections() // 把List<Collection>拆成逐个发射的Observable<Collection> .flatMapObservable { Observable.fromIterable(it) } // 把每个Collection转成Maybe<Book>,自动过滤掉没有数据的Maybe(即Maybe.empty()) .flatMapMaybe(::collectonToBook) // 收集所有发射成功的Book,转换成Single<List<Book>> .toList() }
关键操作符解释
flatMapObservable { Observable.fromIterable(it) }:
把原来的Single<List<Collection>>拆成逐个发射Collection的Observable,这样我们就能逐个处理每个Collection对应的Maybe<Book>,而不是同步生成一堆Maybe对象。flatMapMaybe(::collectonToBook):
这个操作符会自动处理Maybe的两种情况:- 如果
Maybe发射了Book,就把这个Book继续往下游发送 - 如果
Maybe直接调用了onComplete(没有数据),就自动忽略这个Maybe,不会往下游发送任何内容
- 如果
toList():
把下游所有发射的Book收集成一个List,最终返回Single<List<Book>>,完美符合你的需求。
可选优化:保留顺序
如果需要严格保持原来Collection列表的顺序(flatMapMaybe可能会因为异步操作打乱顺序),可以把flatMapMaybe换成concatMapMaybe:
fun getBooks(): Single<List<Book>> { return getCollections() .flatMapObservable { Observable.fromIterable(it) } .concatMapMaybe(::collectonToBook) // 按顺序处理每个Maybe,保证结果顺序和原列表一致 .toList() }
处理空Maybe的场景
如果你不想过滤掉空的Maybe,而是想给一个默认的Book值,可以在collectionToBook之后加上defaultIfEmpty:
.flatMapMaybe { collectionToBook(it).defaultIfEmpty(Book(id = -1, name = "默认书籍")) }
这样即使某个Collection转成Maybe后没有数据,也会用你指定的默认值填充,而不是被过滤掉。
内容的提问来源于stack exchange,提问作者mXaln

