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

RXJava/Kotlin - Chaining Single results in one

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

我有这两种方法:

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

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

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

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

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

在这个方法中,我需要调用前两个来 return 一个 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         
}

您可以使用 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      
}

并行解决方案

要合并多个数据源,最好的运算符是 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并行,并在每个observable完成后执行合并函数。

如果你有更多的 observable 可以压缩,你可以使用 Function3,Function4,...

顺序解决方案(编辑)

如果因为需要顺序执行请求而无法并行执行,那么可以使用 flatmap 函数的 resultSelector。它在平面图之前采用项目,在平面图之后采用分组集合。这样你就可以很容易地创建你的模型组,而不会混淆使用 Pair

唯一的问题是:Single不支持这种平面图。 您可以通过将 return 类型从 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)
    }