Mike6679
Mike6679

Reputation: 6117

RxJava flatMapIterable with concatMap

I have 2 retrofit calls I need to make A & B:

(A): returns an ArrayList

(B): gets the result of (A) which is an ArrayList. (B) iterates through the ArrayList and makes a retrofit call using each and combines the resulting data into a final ArrayList that is sent to my subscriber onNext()

I was able to get it working with flatmap, but the solution is not very elegant. As I understand it, a better solution would be to use flatMapIterable with concatMap but I can't seem to adopt what I have working to flatMapIterable with concatMap.

ServiceFactory.createRetrofitService().getUserFollowing(sessionMgr.getAuthToken())
                .flatMap(new Func1<UserSelfFollows, Observable<? extends ArrayList<Media.MediaData>>>() {
                    @Override
                    public Observable<? extends ArrayList<Media.MediaData>> call(UserSelfFollows userSelfFollows) {

                        //make second requests based on response from First request to get all Users
                        ArrayList<Media.MediaData> arAllMedia = new ArrayList<>();
                        for(UserSelfFollows.UserDataFollows user : userSelfFollows.userdata){

                            Response <ResponseBody> response ;
                            Call <ResponseBody> call;
                            try {
                                call = ServiceFactory.createRetrofitService().getMediaOfUser(user.id,sessionMgr.getAuthToken());
                                response =  call.execute();
                            }catch(IOException ex){
                                return Observable.error(ex);
                            }

                            if (response.isSuccessful()) {

                                try {
                                    String str = responseHelper.streamToString( response.body().byteStream());
                                    Gson gson = new GsonBuilder().create();
                                    Media media = gson.fromJson(str, Media.class);

                                    arAllMedia.addAll(media.mediaData);

                                } catch (IOException e) {
                                    return Observable.error(e);
                                }
                            } else {
                                return Observable.error( new Exception(  responseHelper.getErrorString( response.errorBody().byteStream())) );
                            }
                        }
                        return Observable.just(arAllMedia);

                    }
                })
                .subscribeOn(Schedulers.newThread())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Subscriber<ArrayList<Media.MediaData>>() {
                    @Override
                    public final void onCompleted() {

                    }

                    @Override
                    public final void onError(Throwable e) {

                    }

                    @Override
                    public final void onNext(ArrayList<Media.MediaData> arMedia) {

                    }
                })

Here is what I have so far, but it won't compile:

ServiceFactory.createRetrofitService().getUserFollowing(sessionMgr.getAuthToken())
            .flatMapIterable(new Func1<UserSelfFollows, Iterable<?>>() {
                @Override
                public Iterable<?> call(UserSelfFollows userSelfFollows) {
                    return userSelfFollows.userdata;
                }
            })
            .<Media.MediaData>flatMap(new Func1<UserSelfFollows.UserDataFollows, Observable<Media.MediaData>>() {
                @Override
                public Observable<Media.MediaData> call(UserSelfFollows.UserDataFollows user) {
                    return ServiceFactory.createRetrofitService().getMediaOfUser(user.id, sessionMgr.getAuthToken());
                }
            })
            .toList();

The error is:

enter image description here

Upvotes: 2

Views: 2065

Answers (3)

yosriz
yosriz

Reputation: 10267

If I understand correctly your scenario you can use flatMapIterable, and then flatMap operators, at the end collect all the retrofit calls result with toList.
In the first flatMapIterable you're flatting the UserDataFollows List you get from the first call (getUserFollowing()) to an Obsevrable that emits multiple items from this List, then flatMap create an Observable that makes the retrofit call for each UserSelfFollows.UserDataFollows data object, (it will happens in parallel which seems more suits here, but you can also use concatMap if you interested in sequential execution), then to collect all final data together, as a list of MediaData objects, you can use the toList

ServiceFactory.createRetrofitService().getUserFollowing(sessionMgr.getAuthToken())
            .flatMapIterable(new Func1<UserSelfFollows, Iterable<?>>() {
                @Override
                public Iterable<?> call(UserSelfFollows userSelfFollows) {
                    return userSelfFollows.userdata;
                }
            })
            .flatMap(new Func1<UserSelfFollows, Observable<Media>>() {
                @Override
                public Observable<Media> call(UserSelfFollows user) {
                    return ServiceFactory.createRetrofitService().getMediaOfUser(user.id, sessionMgr.getAuthToken());
                }
            })
            .toList()
            .subscribe(new Action1<List<Media>>() {
                @Override
                public void call(List<Media> rs) {
                    //do something with the list of media data
                }
            });

Upvotes: 2

Mike6679
Mike6679

Reputation: 6117

Yosriz's answer put me in the right direction. So to avoid others pulling there hair out with this, here is the FULL code solution:

ServiceFactory.createRetrofitService().getUserFollowing(sessionMgr.getAuthToken())
                .flatMapIterable(new Func1<UserSelfFollows, Iterable<UserSelfFollows.UserDataFollows>>() {
                    @Override
                    public Iterable< UserSelfFollows.UserDataFollows > call(UserSelfFollows userSelfFollows) {
                        return userSelfFollows.userdata;
                    }
                })
                .flatMap(new Func1<UserSelfFollows.UserDataFollows, Observable<Media>>() {
                    @Override
                    public Observable<Media> call(UserSelfFollows.UserDataFollows user) {
                        return ServiceFactory.createRetrofitService().getMediaOfUser(user.id, sessionMgr.getAuthToken());
                    }
                })
                .toList()
                .subscribeOn(Schedulers.newThread())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Subscriber<List<Media>>() {
                    @Override
                    public final void onCompleted() {

                    }

                    @Override
                    public final void onError(Throwable e) {

                        userMessageHandler.showDialog(mParentActivity, mParentActivity.getString(R.string.error_retrieving_data_title),
                                mParentActivity.getString(R.string.error_self_following_media) + e.getMessage(), 0);
                    }

                    @Override
                    public final void onNext(List<Media> arMedia) {

                        if (arMedia.size() == 0)
                            userMessageHandler.showToast(mParentActivity, mParentActivity.getString(R.string.warn_no_following));
                        else {

                            ArrayList<Media.MediaData> allMedia = new ArrayList<>();
                            for(Media media : arMedia){
                                allMedia.addAll(media.mediaData);
                            }
                            mBinding.gridview.setAdapter(new MediaGridViewAdapter(mParentActivity,FollowingViewModel.this, allMedia));
                        }
                    }
                });

Upvotes: 0

Blackbelt
Blackbelt

Reputation: 157437

I never used both flatMapIterable or concatMap, but as alternative solution you could use the flatMap and compose operator:

ServiceFactory.createRetrofitService().getUserFollowing(sessionMgr.getAuthToken())
   .flatMap(list -> Observable.from(list))
   .compose(getComposer())
   .subscribe(mediaData -> mMediaDataList.add(mediaData), throwable -> {}, () -> { // on complete do something with mMediaList});

Where getComposer() returns a Transformer UserSelfFollows -> Media.MediaData

protected Observable.Transformer<UserSelfFollows, Media.MediaData> getComposer() {
    return  ;
}

Upvotes: 1

Related Questions