Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android/RxJava How to chain network requests and retry when it fails

I am trying to chain network requests using RxJava in Android, and then retry when it fails. I've been looking over StackOverflow on how to do this without getting into Callback hell that comes with using vanilla Android and Retrofit alone. I can do this all in a procedure, in an asynchronous task like this (below are the steps that I need to make) to log in.

  1. Login a user with username/password
  2. Request SSO token if that response is successful
  3. Call a launch service with that sso token

I've been doing this in an AsyncTask which worked only when signing in. However, the SSO token expires while the application is in use, so I need to make the request every time I see that it expires.

The code for the AsyncTask looks like this:

private class LoginUserTask extends AsyncTask<Void, Void, Void> {
    private final String LOG_TAG = LoginUserTask.class.getSimpleName();

    @Override
    protected Void doInBackground(Void... params) {
        OkHttpClient httpClient = ((TribeSocial) getApplication()).getHttpClient();

        // Login User
        GroupDockService groupDockLoginService =
                GroupDockServiceGenerator
                        .createService(GroupDockService.class,
                                httpClient, GroupDockUser.class, new GroupDockUserDeserializer());
        GroupDockUser groupDockUser = groupDockLoginService.loginUser("Tribe", username, password);
        Utility.saveAccountSubdomain(mContext, groupDockUser.getGroupDockSubdomain().getSubdomain());

        // Get Sso Token
        GroupDockService groupDockService = GroupDockServiceGenerator
                .createService(GroupDockService.class, httpClient);
        GroupDockSsoResponse ssoResponse =
                groupDockService.getSsoToken(Utility.getAccountSubdomain(mContext), true);
        Utility.saveSsoToken(mContext, ssoResponse.getSsoToken());

        // Sign in user into Tribe service
        TribeSocialService tribeSocialLaunchService =
                TribeServiceGenerator.createService(TribeSocialService.class,
                        httpClient, new LenientGsonConverter(new Gson()));
        tribeSocialLaunchService.launch(Utility.getSsoToken(mContext));

        // Get User id and save it to SharedPreferences
        TribeSocialService tribeSocialWhoAmIService =
                TribeServiceGenerator.createService(TribeSocialService.class, httpClient,
                        User.class, new WhoAmIDeserializer());
        User tribeUser = tribeSocialWhoAmIService.whoami();
        Utility.saveUserId(mContext, tribeUser.getId());

        return null;
    }

    @Override
    protected void onPostExecute(Void v) {
        Utility.launchMainActivity(mContext);
    }
}

My attempt at making this work with RxJava is something like

GroupDockService groupDockLoginService =
        GroupDockServiceGenerator
                .createService(GroupDockService.class,
                        mHttpClient, GroupDockUser.class, new GroupDockUserDeserializer());

groupDockLoginService
        .loginUserRx("Tribe", username, password)
        .subscribeOn(Schedulers.newThread())
        .observeOn(AndroidSchedulers.mainThread())
        .subscribe(new Subscriber<GroupDockUser>() {
            @Override
            public void onCompleted() {

            }

            @Override
            public void onError(Throwable e) {
                Log.e(LOG_TAG, "I have errored out: " + e.getMessage());
                Toast.makeText(mContext,
                        getString(R.string.username_and_or_password_is_incorrect),
                        Toast.LENGTH_SHORT).show();
            }

            @Override
            public void onNext(GroupDockUser groupDockUser) {
                Utility.saveUsernamePassword(mContext, username, password);
                new LoginUserTask().execute();
            }
        });

In that code, I need to do the following

  1. When loginUserRx succeeds, I need to save the username and password into SavedPreferences
  2. I then need to make a network request to get the sso token
  3. After that, I need to make the third and final network request to launch.

The code right now simply makes a call to login the user, and once that succeeds, kicks off the async task. Ideally, I would like to chain the requests all together in that one request. I've been experimenting with flatmap, map, and etc., and can't figure out how to chain these calls as I stepped out above.

Can anyone shine some light on this? Thanks.

like image 684
Chris Jeon Avatar asked Feb 15 '16 02:02

Chris Jeon


1 Answers

Take a look at this example. Hope, it will inspire you.

service
        //do login
        .loginUser()
        //side effect: save into Shared Preference
        .doOnNext( )
        //get SSO
        .flatMap()
        //do any other side effects
        .doOnNext()
        //do final network request
        .flatMap()
        //in case of error retry after 5 seconds
        .retryWhen(observable -> Observable.timer(5, TimeUnit.SECONDS))
        .subscribeOn(Schedulers.computation())
        .observeOn(AndroidSchedulers.mainThread())
        //update you UI
        .subscribe();

Further reading about retry

like image 179
MyDogTom Avatar answered Nov 04 '22 19:11

MyDogTom