android - 使用 RxJava 链接 Retrofit 调用

标签 android retrofit rx-java

我正试图让自己进入 RxJava,所以我阅读了一些关于它的帖子。我想我理解它是如何工作的,但我想向您提交一个理论代码以确保我对这个库有很好的理解。

让我们想象一下,我有一个 API 可以检索制作人列表以及他们每个人制作的艺术家、他们的专辑和他们的歌曲。 我的模型如下

public class Produceur {
    private Integer id;
    private String name;
    private String pictureUrl;
}

public class Artist {
    private Integer id;
    private String name;
    private String lastname;
    private String sceneName;
    private String pictureUrl;
}

public class Album {
    private Integer id;
    private int year;
    private String name;
    private String style;
    private String pictureUrl;
    private Integer artistId;
}

public class Song {
    private Integer id;
    private String title;
    private int duration;
    private String pictureUrl;
    private Integer albumId;
}

使用我的 Retrofist 服务

@GET("myUrl/produceurs")
Observable<List<ProduceurResponse>> getProduceurs();

@GET("myUrl/produceurs/{produceurId}")
Observable<List<ArtisteResponse>> getArtistForProduceur(@Path("produceurId") Integer produceurId);

@GET("myUrl/picture/{id}")
Observable<ResponseBody> getPicture(@Path("id") Integer id);

和响应对象

public class ProduceurResponse {
    private Integer id;
    private String name;
    private String pictureUrl;
}

public class ArtisteResponse {
    private Integer id;
    private String name;
    private String lastname;
    private String sceneName;
    private String pictureUrl;
    private List<AlbumResponse> albums;
}

public class AlbumResponse {
    private Integer id;
    private int year;
    private String name;
    private String style;
    private String pictureUrl;
    private Integer artistId;
    private List<SongResponse> songs;
}

public class SongResponse {
    private Integer id;
    private String title;
    private int duration;
    private String pictureUrl;
    private Integer albumId;
}

我想检索所有制作人和每个艺术家,并以相同的顺序将所有图像保存在本地内存中。 我想到了以下代码,我们将在其中为每个制作人检索艺术家、他们的专辑和歌曲,并将它们插入我们的库中。 一旦我们完成检索制作人和艺术家,我们就可以下载图像(我们将每个 id 和一张图片存储在一个列表中)。

getProduceurs().flatMap(produceurs -> Observable.from(produceurs))
               .doOnNext(produceur -> insertProduceurInBase(produceur))
               .subscribe(produceur -> Observable.from(getArtistForProduceur(produceur.getId())
                                                 .flatMap(artists -> Observable.from(artists))
                                                 .doOnNext(artist -> insertArtistInBase(artist)))
                                                 .subscribe(),

                          e -> showError(e),

                          () -> Observable.from(listOfIdsToDownload)
                                          .doOnNext(id -> getPicture(id))
                                          .subscribe(response -> createImage(response),

                                          e -> showError(e),

                                          () -> isFinished()
                                          )
                         );

此代码是否有效(我认为是,但我不确定)?这是最好的方法吗?

现在,如果我的 getProduceurs 服务向我返回一个包含制作人艺术家 ID 的 ProduceurResponse 列表,并且我得到了一个检索艺术家个人资料的服务和另一个检索其专辑的服务。

public class ProduceurResponse {
    private Integer id;
    private String name;
    private String pictureUrl;
    List<Integer> artistsIds;
}

public class ArtisteProfileResponse {
    private Integer id;
    private String name;
    private String lastname;
    private String sceneName;
    private String pictureUrl;
}

@GET("myUrl/artist/{artistId}")
Observable<List<ArtisteProfileResponse>> getArtistProfile(@Path("artistId") Integer artistId);

@GET("myUrl/artist/{artistId}/detail")
Observable<List<AlbumResponse>> getArtistAlbums(@Path("artistId") Integer artistId);

我可以使用 .zip 使 getArtistProfile() 和 getArtistAlbums() 调用同时进行,例​​如

getProduceurs().flatMap(produceurs -> Observable.from(produceurs))
               .doOnNext(produceur -> insertProduceurInBase(produceur))
               .subscribe(produceur -> Observable.from(produceur.getArtistIds())
                                                 .zip(
                                                        getArtistProfile(),
                                                        getArtistAlbums(),
                                                        (artistProfil, albumList) -> insertArtistInBase()
                                                 )
                                                 .subscribe(),

                          e -> showError(e),

                          () -> Observable.from(listOfIdsToDownload)
                                          .doOnNext(id -> getPicture(id))
                                          .subscribe(response -> createImage(response),

                                          e -> showError(e),

                                          () -> isFinished()
                                          )
                         );

但我真的不确定我是否以正确的方式使用 zip。 zip 在这段代码中使用得好吗?这行得通吗?这是最好的方法吗?

编辑

所以我尝试使用 google books api 实现与我最初的想法类似的东西。

我有一个 Retrofit 界面

public interface IBookService {
    @GET("volumes?q=robot+subject:fiction")
    Observable<BookSearchResult> getFictionAuthors(@Query("category") String key);
    @GET("volumes")
    Observable<BookSearchResult> getBooksForAuthor(@Query("q") String author, @Query("category") String key);
}

public class BookSearchResult {
    public List<BookResult>  items;
}

public class BookResult {
    public String id;
    public String selfLink;
    public VolumeInfoResult volumeInfo;
    public SaleInfoResult saleInfo;
}

然后我尝试使用字符串机器人 (getFictionAuthors) 检索小说书籍,并返回一个包含 BookResult 列表的 BookSearchResult。对于每本书的结果,我使用 getBooksForAuthor 检索作者的所有书籍。我的代码在下面

Observable<BookResult> observable = mWebService.getFictionAuthors(API_KEY)
                .flatMap(new Func1<BookSearchResult, Observable<BookResult>>() {

                    // Parse the result and build a CurrentWeather object.
                    @Override
                    public Observable<BookResult> call(final BookSearchResult data) {
                        return Observable.from(data.items);
                    }
                })
                .concatMap(new Func1<BookResult, Observable<BookSearchResult>>() {

                    // Parse the result and build a CurrentWeather object.
                    @Override
                    public Observable<BookSearchResult> call(final BookResult data) {
                        return mWebService.getBooksForAuthor("=inauthor:" + data.volumeInfo.authors.get(0), API_KEY);
                    }
                })
                .flatMapIterable(new Func1<BookSearchResult, List<BookResult>>() {

                    // Parse the result and build a CurrentWeather object.
                    @Override
                    public List<BookResult> call(final BookSearchResult data) {
                        return data.items;
                    }
                })
                .subscribeOn(Schedulers.newThread())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Subscriber<BookResult>() {
                    @Override
                    public void onNext(final BookResult book) {
                        Log.e("Book","Book is " + book.volumeInfo.title + " written by " + book.volumeInfo.authors.get(0));
                    }

                    @Override
                    public void onCompleted() {
                        Log.e("Book","Book list completed");
                    }

                    @Override
                    public void onError(final Throwable error) {
                        Log.e("Book","Book list error");
                    }
                }

这段代码可以正常工作,但有一些我不明白的奇怪之处。在我的日志中,我首先从 getBooksForAuthor 请求返回第一作者,然后是该作者每本书的日志。之后,我得到了第二作者的请求结果和他书中的部分日志。按照其他作者请求的结果,然后是第二作者的图书列表末尾和所有其他作者的图书列表。

为了说明这一点,我的日志看起来像

- > Return from request for Author 1
- > Book 1 from author 1
...
- > Book 10 from author 1
- > Return from request for Author 2
- > Book 1 from author 2
...
- > Book 5 from author 2
- > Return from request for Author 3
- > Return from request for Author 4
...
- > Return from request for Author 10
- > Book 6 from author 2
..
- > Book 10 from author 2
- > Book 1 from author 3
..
- > Book 10 from author 3
...
- > Book 1 from author 10
..
- > Book 10 from author 10

如我所料

- > Return from request for Author 1
- > Book 1 from author 1
...
- > Book 10 from author 1
- > Return from request for Author 2
- > Book 1 from author 2
...
- > Book 10 from author 2
...
- > Return from request for Author 10
- > Book 1 from author 10
...
- > Book 10 from author 10

有人有解释或理解我遗漏了什么吗?

最佳答案

您应该避免嵌套订阅(查看平面图运算符)。它通常是代码异味的一个指标。 为避免嵌套订阅,您可以使用运算符 flatMap(不保证结果事件顺序)或 concatMap(保证结果事件顺序)。

我也注意到您在完成的回调中使用了另一个 Observable:在这种情况下您可以concat observable。

因此使用此类运算符重新编写代码:

 getProduceurs().flatMap(produceurs -> Observable.from(produceurs))
                .doOnNext(produceur -> insertProduceurInBase(produceur))
                // call getArtistForProduceur and emit results in order
                .concatMap(produceur -> getArtistForProduceur(produceur.getId()))
                // emits items of the list
                .flatMapIterable(artists -> artists)
                .doOnNext(artist -> insertArtistInBase(artist)))
                // don't care about elements. But will wait for the completion of the previous observable
                .ignoreElements()
                // perform jobs after the previous observable complete
                .concatWith(Observable.from(listOfIdsToDownload)
                                      .doOnNext(id -> getPicture(id))
                                      .doOnNext(response -> createImage(response)))
                // show an error if an error occur in the downstream
                .doOnError(e -> showError(e))
                // call isFinished when everything is finished.
                .doOnCompleted(() -> isFinished())
                .subscribe()

关于android - 使用 RxJava 链接 Retrofit 调用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41018463/

相关文章:

java - Spring 中新线程上的 Rx java 本地化问题

java - 如何清理 Rx Subscription 和 Observable 以防止内存泄漏?

android - 是否有必要使用 Android SDK Manager 安装旧版本的 Android SDK?

android - Google Play 商店 --- 此商品在您所在的国家/地区不可用

java - 如何在Retrofit 2.0+中正确设置注释和查询?

android - RxJava : Know when observable is finished and return Boolean Observable to the caller

android - 在运行时询问 WebView 的摄像头权限

java - 推荐的数字签名加密组合

java - 使用改造转换 json 数组?

java - Android 单个观察者,在不同的类中有多个订阅者