RXJava / Kotlin - Цепные одиночные результаты в одном - PullRequest
1 голос
/ 13 января 2020

У меня есть проблема, и я не знаю, как решить ее с лучшим подходом. Проблема в том, что я запрашиваю Spotify Web API, и в некоторых методах возвращается изображение исполнителя, а в других - только базовая c информация об исполнителе.

У меня есть два метода:

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

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

Когда я получаю альбом, информация об исполнителе не содержит URL-адрес изображения исполнителя. Итак, мне нужно вызвать метод getAlbum () и использовать результат для получения artistId, а затем вызвать метод getArtist ().

У меня есть следующий метод, чтобы сделать все это:

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

В этом методе мне нужно вызвать два предыдущих, чтобы вернуть объект 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         
}

Ответы [ 2 ]

1 голос
/ 13 января 2020

ПАРАЛЛЕЛЬНОЕ РЕШЕНИЕ

Для объединения нескольких источников данных лучшим оператором является zip :

Rx Zip operator

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

Он будет запускать все наблюдаемые параллельно и выполнять функцию слияния после завершения каждой наблюдаемой.

Если у вас есть больше наблюдаемых для zip, вы можете использовать Function3, Function4, ...

ПОСЛЕДОВАТЕЛЬНОЕ РЕШЕНИЕ (РЕДАКТИРОВАТЬ)

Если параллельное выполнение невозможно, так как вам требуется, чтобы запрос выполнялся последовательно, вы можете использовать resultSelector функции плоской карты. Он берет элемент перед flatMap и сгруппированную коллекцию после flatmap. Таким образом, вы легко сможете создать свою группу моделей, не вводя в заблуждение использование Pair

Flatmap with resultSelector

Единственный улов: Single не поддерживает этот вид плоской карты. Вы можете обойти эту проблему, либо изменив тип возврата с Single на Observable, либо просто конвертируйте Single во время выполнения с оператором toObservable.

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)
    }
1 голос
/ 13 января 2020

Вы можете использовать Pair с flatMap, чтобы обернуть результаты вместе:

_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      
}
...