neonDion
neonDion

Reputation: 2358

Getting variables "into" RxJava chain for use later

I am using RxJava on Android to perform a login operation.

I need to pass in a username, password and a boolean flag. The username and password and sent to a server for verification and once a response is returned I need to use the flag to determine what to do next.

Since the login operation is asynchronous, I want to ensure that when the response returns that I will still have access to the username, password and flag that I passed in at the beginning.

Here is the initial way I coded this up that I believe has problems:

    public Observable<Result> execute1(final String username, final String password, final boolean shouldSaveUsername) {
    return mLoginNetwork
            .loginWithCredentials(username, password)
            .map(new Func1<Response<Void>, LoginObject>() {
                @Override
                public LoginObject call(Response<Void> response) {
                    if (!response.isSuccessful()) {
                        Exceptions.propagate(new HttpException(response));
                    }

                    return new LoginObject(username, password, shouldSaveUsername);
                }
            })
            .doOnNext(new Action1<LoginObject>() {
                @Override
                public void call(LoginObject loginObject) {
                    if (loginObject.shouldSaveUsername) {
                        saveUsername(username);
                    }
                }
            })
            .flatMap(new Func1<Entitlement, Observable<Result>>() {
                @Override
                public Observable<Result> call(LoginObject loginObject) {
                    return mNetwork
                            .fetchSomething();
                }
            });
}

When I call execute1() it returns an Observable which I cache and then subscribe to. If an Android configuration change occurs I unsubscribe from the Observable but keep it in a cache. Once the configuration change is complete I take the Observable out of the cache and resubscribe to it. When I resubscribe the loginWithCredentials call would need to be made again, but when it returns the username, password and boolean flag would no longer exist and therefore I wouldn't be able to use them in my chain which is a problem.

So, how to solve this issue?

I need a way for the input data to the Observable to become part of the Observable so that when I cache the Observable the input data is also cached.

Here is a proposed solution below:

    public Observable<Result> execute2(String username, String password, boolean shouldSaveUsername) {
    return Observable
            .just(new LoginData(username, password, shouldSaveUsername))
            .flatMap(new Func1<LoginData, Observable<LoginData>>() {
                @Override
                public Observable<?> call(final LoginData loginData) {
                    return mLoginNetwork
                            .loginWithCredentials(loginData.getUsername(), loginData.getPassword())
                            .map(new Func1<Response<Void>, LoginData>() {
                                @Override
                                public LoginData call(Response<Void> response) {
                                    if (!response.isSuccessful()) {
                                        Exceptions.propagate(new HttpException(response));
                                    }
                                    return loginData;
                                }
                            });
                }
            })
            .doOnNext(new Action1<LoginData>() {
                @Override
                public void call(LoginData loginData) {
                    if (loginData.shouldSaveUsername) {
                        saveUsername(username);
                    }
                }
            })
            .flatMap(new Func1<LoginData, Observable<Result>>() {
                @Override
                public Observable<Result> call(LoginData loginData) {
                    return mNetwork
                            .fetchSomething();
                }
            });
}

What I'm attempting to do is to make the input data part of the stream right away by using Observable.just() to take the input data and make it into an Observable and then let the rest of the downstream operations receive it as an input. I assume that if I now cache the observable and resubscribe later that the input data is now embedded in my observable and can be accessed in any of the operators later.

Have I solved my problem in my proposed solution in a "normal" RxJava / functional way? Are there better ways to approach this problem?

Upvotes: 3

Views: 799

Answers (1)

Bob Dalgleish
Bob Dalgleish

Reputation: 8227

The username/password/save-flag are passed in to execute1() as parameters, marked as final. Then, in your anonymous nested classes, you make explicit references to those values, "closing over" them. The resulting observable chain has everything bound to it that it needs in order to operate.

Subscribing to the observable again will use the original username/password/save-flag.

Upvotes: 1

Related Questions