如何在Retrofit+RxJava+MVVM架构下加载多页电影数据
解决启动时加载3页电影数据的问题
首先得点明你之前方案的核心问题:你的Repository复用了同一个mMoviesResultsMutableLiveData实例,每次请求新页面时setValue都会覆盖之前的数据;再加上for循环同时发起3个请求,最终只有最后一次请求的结果会被Observer接收——毕竟LiveData的通知是覆盖式的,而且你也没做RxJava请求的结果合并。
下面给你一步步的重构方案:
第一步:重构Repository,用RxJava合并多页请求
我们可以借助RxJava的操作符,把3页请求的结果合并成一个完整的列表返回。这里用Observable.range生成1-3的页码,再通过concatMap(按顺序执行请求)或flatMap(并行执行请求)串联/并行处理每个请求,最后用reduce合并所有页的数据。
修改后的Repository代码:
public LiveData<List<MoviesResult>> getMultiPageMovies(String sort_by, int pageCount, String with_genres, String with_cast) { MutableLiveData<List<MoviesResult>> combinedLiveData = new MutableLiveData<>(); MoviesService moviesService = RetrofitInstance.getMoviesService(); String apiKey = mApplication.getResources().getString(R.string.api_key); boolean includeAdults = UserSettings.getInstance().includeAdults(); // 生成1到pageCount的页码流,按顺序请求每一页 Observable.range(1, pageCount) .concatMap(page -> moviesService.getMovies(apiKey, sort_by, includeAdults, true, page, with_genres, with_cast) .retryWhen(throwable -> throwable.delay(5, TimeUnit.SECONDS)) .subscribeOn(Schedulers.io())) // 提取每一页的电影列表 .map(MoviesResponse::getMoviesResults) // 将所有页的列表合并成一个大列表 .reduce(new ArrayList<>(), (totalList, pageList) -> { totalList.addAll(pageList); return totalList; }) .observeOn(AndroidSchedulers.mainThread()) .subscribe(combinedList -> { combinedLiveData.setValue(combinedList); }, throwable -> { CustomToast customToast = new CustomToast(mApplication.getApplicationContext()); customToast.makeCustomText(throwable.getMessage()); }); return combinedLiveData; }
注:如果想并行请求可以把
concatMap换成flatMap,但要注意目标API的请求频率限制,避免触发限流。
第二步:更新ViewModel层
ViewModel只需要调用新的Repository方法,直接返回合并后的LiveData即可:
public LiveData<List<MoviesResult>> getMultiPageMovies(String sort_by, int pageCount, String with_genres, String with_cast) { return mMoviesRepository.getMultiPageMovies(sort_by, pageCount, with_genres, with_cast); }
第三步:修改MainActivity的调用逻辑
现在只需要订阅一次,就能拿到3页合并后的所有数据了:
mMoviesViewModel.getMultiPageMovies(getString(R.string.sort_by_popularity), 3, null, null) .observe(this, moviesResults -> { Log.d(TAG, "onChanged: 总数据量" + moviesResults.size()); // 在这里处理合并后的完整电影列表 });
补充:为什么之前的for循环方案失效?
- 你的Repository每次返回同一个
mMoviesResultsMutableLiveData,3个请求同时发起后,最后一次请求的setValue会覆盖前两次的结果,导致Observer只能接收到最后一页的数据。 - 就算你每次创建新的MutableLiveData,for循环里的3个Observer会各自接收单页数据,不仅代码冗余,还需要手动合并数据,远不如RxJava操作符的方式优雅可控。
内容的提问来源于stack exchange,提问作者Michael
相关产品推荐
相关产品推荐

