用RxJava替换嵌套的Retrofit调用

时间:2018-03-15 00:18:53

标签: android rest retrofit rx-java rx-java2

首先,让我先说一下我对RxJava比较陌生,所以请在掌握基础知识时请耐心等待。 :)

我基本上了解整个subscribe[On|With]()初学者类型的东西(或多或少)。我正在尝试做一些有点复杂的事情,JavaRx似乎非常适合这种事情。

我有一个策划的电影数据库,我正在查询电影细节。现在,这里的API有点奇怪。您必须拨打两个电话才能获得有关电影的所有内容。第一个API调用产生id,标题,类别和评级以及第二个其他重要的东西,如描述,预告片等。正如您可能已经猜到的,您需要来自part1的movieId来获取额外的信息。这里嵌套的调用工作,但它们绝不是最佳实践。

我的代码看起来像这样:

pageSingle.subscribeOn(Schedulers.io())
        .subscribeWith(new DisposableSingleObserver<mPage>() {
            @Override public void onSuccess(mPage value) {
                moviesInPage = value.movies;
                if(moviesInPage==null){
                    Log.w(TAG, "mergeMovieParts: no movies returned");
                }else {
                    for (int i = 0; i < moviesInPage.size(); i++) {
                        final fMovie firstMovie = moviesInPage.get(i);
                        apiService.getSecondPart(firstMovie.id)
                                .subscribeWith(new DisposableSingleObserver<sMovie>() {
                            @Override 
                            public void onSuccess(sMovie secondMovie) {
                                mergeMovieParts(firstMovie, secondMovie);
                                }
                            @Override 
                            public void onError(Throwable e) {
                                e.printStackTrace();
                                }
                        });
                    }
                }

            }
            @Override public void onError(Throwable e) {
                handleError(e);
            }
        });

pageSingle也是Single。 你可能会发现为什么不能保证一直都能正常工作。 Single.zip()在这里不起作用,因为我需要从第一次调用开始第二次调用的信息(更具体地id)。我的问题归结为:

  1. 如何用JavaRx替换那些嵌套的Retrofit调用 那些?请注意mPage会返回List<fMovie>个对象。

  2. 此刻,我一个接一个地打这些电话。是吗 可以获取电影列表并基于此来制作多个 同时调用以获取第二部分?我正在猜测 多线程就是这里的答案,但我该怎么做呢 RxJava有什么影响,例如:是不值得的,在你的 意见或是对大的权衡(如果有的话)?

  3. 与此问题无关,但您有什么建议吗? 书籍,视频等我可以阅读/观看,这将有助于我赶上 与RxJava最多。它激动了我很多,但与此同时我 觉得通过“阅读”来学习所有内容有点过于庞大 文档”。

  4. 如果我不够清楚,请不要犹豫,要求更多背景/澄清。提前谢谢!

    编辑flatMap()错误:

    Error:(109, 17) error: no suitable method found for flatMap(<anonymous Function<Movie1,ObservableSource<?>>>)
    method Observable.<R#1>flatMap(Function<? super Object,? extends ObservableSource<? extends R#1>>) is not applicable
    (cannot infer type-variable(s) R#1
    (argument mismatch; <anonymous Function<Movie1,ObservableSource<?>>> cannot be converted to Function<? super Object,? extends ObservableSource<? extends R#1>>))
    method Observable.<R#2>flatMap(Function<? super Object,? extends ObservableSource<? extends R#2>>,boolean) is not applicable
    (cannot infer type-variable(s) R#2
    (actual and formal argument lists differ in length))
    method Observable.<R#3>flatMap(Function<? super Object,? extends ObservableSource<? extends R#3>>,boolean,int) is not applicable
    (cannot infer type-variable(s) R#3
    (actual and formal argument lists differ in length))
    method Observable.<R#4>flatMap(Function<? super Object,? extends ObservableSource<? extends R#4>>,boolean,int,int) is not applicable
    [...]
    

    我的实施:

    movieService.getMoviesFromPath(path)
                    .subscribeOn(Schedulers.io())
                    .flattenAsObservable(new Function<MoviePage, Iterable<?>>() {
                        @Override
                        public Iterable<?> apply(MoviePage moviePage) throws Exception {
                            return moviePage.movieList;  // movieList is a List<Movie1>
                        }
                    })
                    .flatMap(new Function<Movie1, ObservableSource<?>>() {
                        @Override
                        public ObservableSource<?> apply(Movie1 movie1) throws Exception {
                            return null;
                        }
                    });
    

1 个答案:

答案 0 :(得分:1)

您似乎需要以下内容:

movieService.getPage()
    .flattenAsObservable(page -> page.movies)
    .flatMap(movie -> apiService.getSecondPart(movie.movieId))

或者,带走了lambdas,

movieService.getPage()
            .flattenAsObservable(new Function<Page, Iterable<Movie>>() {
                @Override
                public Iterable<Movie> apply(Page page) throws Exception {
                    return page.movies;
                }
            })
            .flatMap(new Function<Movie, ObservableSource<DetailedMovie>>() {
                @Override
                public ObservableSource<DetailedMovie> apply(Movie movie) throws Exception {
                    return apiService.getSecondPart(movie.movieId);
                }
            })

话虽如此,在原帖中写new DisposableSingleObserver<mPage>之类的东西是个坏兆头。在继续使用Rx之前,可能值得花时间学习Generics in Java,因为这是导致第二次错误的原因。

您可能需要具备良好的泛型工作知识(and standard Java naming practices)才能充分利用RxJava。祝你好运!

请注意,这两个示例假定subscribeOn(Schedulers.io())是隐式的并且在Retrofit级别配置。您可以看到如何执行此操作in these answers

更新:如果你想将两个Movie对象合并在一起,我想你可以这样做:

    movieService.getPage()
            .flattenAsObservable(new Function<Page, Iterable<? extends Movie>>() {
                @Override
                public Iterable<? extends Movie> apply(Page page) throws Exception {
                    return page.movies;
                }
            })
            .flatMap(new Function<Movie, ObservableSource<DetailedMovie>>() {
                @Override
                public ObservableSource<DetailedMovie> apply(Movie movie) throws Exception {
                    return apiService.getSecondPart(movie.movieId).toObservable();
                }
            }, new BiFunction<Movie, DetailedMovie, MergedMovie>() {
                @Override
                public MergedMovie apply(Movie movie, DetailedMovie detailedMovie) throws Exception {
                    return new MergedMovie(movie, detailedMovie);
                }
            });
相关问题