简体   繁体   中英

RxJava: Pause Code Execution Until Items Are Emitted

I am just starting to learn about RxJava and have been having some struggles with it lately. I have a section of code that needs to be done using RxJava and I don't want the rest of my code to be ran until either onSuccess() or onError() of my observer are called. My current implementation is as follows:

    @Override
    public Bundle getAuthToken(AccountAuthenticatorResponse accountAuthenticatorResponse, Account account, String authTokenType, Bundle options) {
        final AccountManager manager = AccountManager.get(mContext);
        final String username = account.name;
        String token = manager.peekAuthToken(account, authTokenType);

        if (TextUtils.isEmpty(token)){
            final String password = manager.getPassword(account);
            if (password != null){
                LoginClient client = ClientGenerator.createClient(LoginClient.class);
                String encodedString = encodeClientIDAndSecret();

                // Current implementation of observer
                Single<TokenResponse> single = client.getRxAccessToken(encodedString, LoginClient.GRANT_TYPE, account.name, password, LoginClient.SCOPE);
                single.map(new Function<TokenResponse, String>() {
                    @Override
                    public String apply(TokenResponse tokenResponse) throws Exception {
                        return tokenResponse.getAccessToken();
                    }
                })
                        .subscribeOn(Schedulers.io())
                        .observeOn(AndroidSchedulers.mainThread())
                        .subscribe(new DisposableSingleObserver<String>() {
                            @Override
                            public void onSuccess(String accessToken) {
                                Log.d(TAG, "Here is the access token: " + accessToken);
                            }

                            @Override
                            public void onError(Throwable e) {
                                Log.d(TAG, "Unsuccessful response...");
                            }
                        });

            }
        }

        token = manager.peekAuthToken(account, authTokenType);
        if (!TextUtils.isEmpty(token)){
            final Bundle result = new Bundle();
            result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name);
            result.putString(AccountManager.KEY_ACCOUNT_TYPE, account.type);
            result.putString(AccountManager.KEY_AUTHTOKEN, token);
            return result;
        } else {
            final Intent intent = new Intent(mContext, LoginActivity.class);
            intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, accountAuthenticatorResponse);
            intent.putExtra(LoginActivity.EXTRA_ACCOUNT_TYPE, account.type);
            intent.putExtra(LoginActivity.EXTRA_AUTH_TOKEN_TYPE, authTokenType);

            final Bundle bundle = new Bundle();
            bundle.putParcelable(AccountManager.KEY_INTENT, intent);
            return bundle;
        }
    }

Is there any way that I can prevent the rest of my code from running until my observer completes its subscription?

To provide some better context on this, what I'm trying to implement is a custom account authenticator for Android. When the user first logs in, they won't have an auth token, so I obtain a new one using RxJava (as shown above). Then, I need to return a Bundle back to the Android AccountManager , which depends on whether I was able to successfully obtain the token or not.

I'm able to retrieve the access token using RxJava. However, if I try to read the token outside of the observer, the token is null because the request hasn't yet been completed... That's why I'm hoping to somehow pause the execution of the rest of the code until the subscription is complete.

As always, any assistance on this particular issue would be greatly appreciated :)

If this is your Single

Single<TokenResponse> singleResponse = client.getRxAccessToken(
    encodedString,
    LoginClient.GRANT_TYPE,
    account.name,
    password,
    LoginClient.SCOPE
);

then try something like below (The return type of getAuthToken() needs to be changed):

@Override
public Single<Bundle> getAuthToken(AccountAuthenticatorResponse accountAuthenticatorResponse, Account account, String authTokenType, Bundle options) {
    Return singleResponse
        .flatMap(tokenResponse -> createBundle(tokenResponse))
        .subscribeOn(Schedulers.io())
        .observeOn(AndroidSchedulers.mainThread());
}

Now create a method createBundle(TokenResponse tokenResponse) like this

private Single<Bundle> createBundle(TokenResponse tokenResponse) {
    return Single.create(
        e -> {
            String token = tokenResponse.getAccessToken();
            manager.peekAuthToken(account, authTokenType);
            if (!TextUtils.isEmpty(token)){
                final Bundle result = new Bundle();
                result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name);
                result.putString(AccountManager.KEY_ACCOUNT_TYPE, account.type);
                result.putString(AccountManager.KEY_AUTHTOKEN, token);

                e.onSuccess(result);
            } else {
                final Intent intent = new Intent(mContext, LoginActivity.class);
                intent.putExtra(
                    AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, accountAuthenticatorResponse);
                intent.putExtra(LoginActivity.EXTRA_ACCOUNT_TYPE, account.type);
                intent.putExtra(LoginActivity.EXTRA_AUTH_TOKEN_TYPE, authTokenType);
                final Bundle bundle = new Bundle();
                bundle.putParcelable(AccountManager.KEY_INTENT, intent);

                e.onSuccess(bundle);
            }
        }
    );
}

Now who ever does subscribe on getAuthToken() will get Bundle

getAuthToken()
    .subscribe(
        bundle -> //Use bundle as needed,
        error -> //Handle error scenario
    ) 

Hope this answer helps.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM