RXJava/Kotlin - 链接单个结果为一个

Posted

技术标签:

【中文标题】RXJava/Kotlin - 链接单个结果为一个【英文标题】:RXJava/Kotlin - Chaining Single results in one 【发布时间】:2020-04-29 15:55:59 【问题描述】:

我有一个问题,我不知道如何用更好的方法解决它。问题是我正在向 Spotify Web API 请求,并且在某些方法中返回了艺术家图像,而在其他方法中只获得了基本的艺术家信息。

我有这两种方法:

fun getAlbum(albumId: String): Single<SpotifyAlbumDTO>

fun getArtist(artistId: String): Single<SpotifyArtistDTO>

当我获得专辑时,艺术家信息不包含艺术家图片网址。因此,我需要调用 getAlbum() 方法并使用结果获取 artistId,然后调用 getArtist() 方法。

我有以下方法来做所有这些事情:

fun getAlbum(albumId: String): Single<Album>

在这个方法上,我需要调用前两个来返回一个专辑对象(我的域对象)。唯一对我有用的解决方案如下:

fun getAlbum(albumId: String): Single<Album> 
  return Single.create  emitter ->
    _spotifyService.getAlbum(albumId).subscribe  spotifyAlbum ->
      _spotifyService.getArtist(spotifyAlbum.artist.id).subscribe  spotifyArtist ->
        val artistImage = spotifyArtist.imageUrl
        spotifyAlbum.artist.image = artistImage
        emitter.onNext(spotifyAlbum.toAlbum())
      
    
  

我认为必须存在另一种更好的方法来做到这一点,而不是在其他订阅中连接订阅调用并创建更深入的调用。我也尝试以下方法:

_spotifyService.getAlbum(albumId).flatMap  spotifyAlbum ->
  _spotifyService.getArtist(spotifyAlbum.artist.id)
.flatMap  spotifyArtist ->
  // Here I don't have the album and I can't to asign the image         

【问题讨论】:

【参考方案1】:

并行解决方案

要结合多个数据源,最好的运算符是zip:

Single.zip(getAlbum(albumId), getArtist(artistId),
    BiFunction<SpotifyAlbumDTO, SpotifyArtistDTO, SpotifyAlbumDTO>  album, artist -> 
        val artistImage = artist.imageUrl
        album.artist.image = artistImage
        album //return value of the merged observable 
    
).subscribe  album: SpotifyAlbumDTO?, error: Throwable? ->
    emitter.onNext(album.toAlbum())

它将并行运行所有的可观察对象,并在每个可观察对象完成后执行合并功能。

如果你有更多的 observable 要 zip,你可以使用 Function3、Function4、...

顺序解决方案(编辑)

如果由于需要顺序执行请求而无法并行执行,则可以使用 flatmap 函数的resultSelector。它采用 flatMap 之前的项目和 flatmap 之后的分组集合。这样您就可以轻松创建模型组,而不会混淆 Pair

唯一的问题是:Single 不支持这种平面图。 您可以通过将返回类型从 Single 更改为 Observable 或在运行时使用 toObservable 运算符转换您的 Single 来解决此问题。

getAlbum(albumId)
    .toObservable()
    .flatMap(album: SpotifyAlbumDTO -> getArtist(album.artist.id).toObservable(),
              album:SpotifyAlbumDTO, artist:SpotifyArtistDTO ->
                 album.artist.image = artist.imageUrl
                 album
             )
    ?.subscribe  album: SpotifyAlbumDTO ->
        print(album)
    

【讨论】:

抱歉,我不能使用 zip,因为在执行 getAlbum() 之前我不知道艺术家 ID 我误解了你的问题,我已经更新了我的答案 谢谢!这就是我需要的【参考方案2】:

您可以使用PairflatMap 将您的结果包装在一起:

_spotifyService.getAlbum(albumId).flatMap  spotifyAlbum ->
  _spotifyService.getArtist(spotifyAlbum.artist.id)
       .flatMap  artist ->
           Single.just(spotifyAlbum, artist)
       
.subscribe  pair: Pair<Album, Artist> ->
  // grab results
  val album = pair.first
  val artist = pair.second      

【讨论】:

谢谢,这是一个很棒的方法!我想知道其他解决方案,我认为必须存在一个运算符来积累以前的结果,因为如果你想连接更多的电话,我认为这不是最好的。这不是一个奇怪的案例,必须是更好的解决方案!

以上是关于RXJava/Kotlin - 链接单个结果为一个的主要内容,如果未能解决你的问题,请参考以下文章

RxJava/Kotlin Observable 方法调用链 - 如何终止?

kotlin 中的 Observable.combineLatest 类型推断

laravel 7.14 在使用关系搜索数据表后为产品构建链接

如何链接 Django 查询集以保留单个顺序

在 Tomcat 中为单个目录配置符号链接

ajax提交数据,spring Mvc按照单个参数接收