Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Chaining Singles together in RxJava

Tags:

I am using RxJava in my android app along with Retrofit to make network requests to a server. I am using RxJavaCallAdapterFactory so I can have my retrofit requests return singles. In my code, the retrofit object is named 'api'.

The code here works fine, but in this example, I need to retrieve the userId before I can make a playlist. I flat map the userId request to the API request, and after making the playlist, I need to use flat map again to convert the JSON response to a usable object.

public JSONUser me;

public Single<String> getUserId(){

    if(me != null){
        return Single.just(me.getUserId());
    }

    return api.getMe().flatMap(new Func1<JSONUser, Single<String>>() {
        @Override
        public Single<String> call(JSONUser meResult) {
            me = meResult;
            return Single.just(me.getUserId());
        }
    });
}

public Single<Playlist> createPlaylist(String name) {

    final NewPlaylistConfig config = new NewPlaylistConfig(name);

    return getUserId().flatMap(new Func1<String, Single<Playlist>>() {
        @Override
        public Single<Playlist> call(String userId) {
            return api.createPlaylist(userId, config).flatMap(
                    new Func1<JSONPlaylist, Single<? extends SpotifyPlaylist>>() {
                        @Override
                        public Single<? extends Playlist> call(JSONPlaylist data) {
                            return Single.just(new Playlist(data));
                    }
            });
        }
    });
}

The entry point here would be createPlaylist(). NewPlaylistConfig will be converted to JSON and is simply the body parameter for the POST request. UserId is needed as a path parameter.

My main question here, is if there is a way to chain these operations without the "callback-hell" you see here. Like I said, this code works but it is really ugly. I would appreciate if somebody could point me in the right direction regarding this. Is there a way to make this work like promises where you can just chain .thens?

Thank you.

like image 200
Jtvd78 Avatar asked Feb 08 '17 06:02

Jtvd78


People also ask

How does RxJava chain work?

subscribeOn is executed during the subscription process and affects both upper, and lower streams. It also can change the thread as many times as you write it. The one closest to the top of the chain is applied. observeOn affects only the lower streams, and is executed during emission.

What's the difference between MAP and FlatMap () in RxJava?

Map transforms the items emitted by an Observable by applying a function to each item. FlatMap transforms the items emitted by an Observable into Observables. So, the main difference between Map and FlatMap that FlatMap mapper returns an observable itself, so it is used to map over asynchronous operations.

What is single RxJava?

Single. Single is an Observable that always emit only one value or throws an error. A typical use case of Single observable would be when we make a network call in Android and receive a response. Sample Implementation: The below code always emits a Single user object.

How does RxJava work internally?

RxJava is a Java VM implementation of ReactiveX a library for composing asynchronous and event-based programs by using observable sequences. The building blocks of RxJava are Observables and Subscribers. Observable is used for emitting items and Subscriber is used for consuming those items.


1 Answers

Instead of:

getUserId()
    .flatMap(new Func1<String, Single<Playlist>>() {
        @Override
        public Single<Playlist> call(String userId) {
            return api.createPlaylist(userId, config).flatMap(
                new Func1<JSONPlaylist, Single<? extends SpotifyPlaylist>>() {
                    @Override
                    public Single<? extends Playlist> call(JSONPlaylist data) {
                        return Single.just(new Playlist(data));
                    }
            });
        }
     });

write:

getUserId()
    .flatMap(new Func1<String, Single<Playlist>>() {
        @Override
        public Single<Playlist> call(String userId) {
            return api.createPlaylist(userId, config);
        }
    })
    .flatMap(
        new Func1<JSONPlaylist, Single<? extends SpotifyPlaylist>>() {
            @Override
            public Single<? extends Playlist> call(JSONPlaylist data) {
                return Single.just(new Playlist(data));
            }
    });

Actually you can chain flatmap (and map) like you chain then with promises.


Note you can replace the second flatmap with map, replacing:

return Single.just(new Playlist(data));

with:

return new Playlist(data);

Result :

getUserId()
    .flatMap(new Func1<String, Single<Playlist>>() {
        @Override
        public Single<Playlist> call(String userId) {
            return api.createPlaylist(userId, config);
        }
    })
    .map(
        new Func1<JSONPlaylist, SpotifyPlaylist>() {
            @Override
            public SpotifyPlaylist call(JSONPlaylist data) {
                return new Playlist(data);
            }
    });

Edit: you can go further with java 8:

getUserId()
    .flatMap(userId -> api.createPlaylist(userId, config))
    .map(data -> new Playlist(data));

Or Kotlin:

getUserId()
    .flatMap { api.createPlaylist(it, config) }
    .map { Playlist(it) }
like image 145
Geoffrey Marizy Avatar answered Oct 05 '22 02:10

Geoffrey Marizy