【问题标题】:RxJava Android wait for callback to finish before returning dataRxJava Android 在返回数据之前等待回调完成
【发布时间】:2017-09-29 16:40:02
【问题描述】:

我是 RxJava 新手。我有一个使用 AWS Cognito SDK 进行身份验证的 Android 应用程序。我有一个AwsAuthClient 类来处理调用 SDK 并返回结果。我有一个片段调用AwsAuthClient 中的SignUp 方法。我需要将注册结果返回给片段,以便它能够做出适当的反应。

RegisterFragment 类:

public class RegisterFragment{
    AwsAuthClient authClient;

    public void onCreateAccountClick() {
        Subscription createSubscription = authClient.SignUp(params)
            .compose(Transformers.applyIoToMainSchedulers())
            .subscribe((CognitoUser currentUser) -> {
                transitionToVerificationScreen();
             }, (Throwable throwable) -> {
                 // Report the error.
             });
    }
}

这是 AwsAuthClient:

public class AwsAuthClient {

    public void SignUp(CreateParams createParams){

        // Create a CognitoUserAttributes object and add user attributes
        CognitoUserAttributes userAttributes = new CognitoUserAttributes();

        // Add the user attributes. Attributes are added as key-value pairs
        // Adding user's given name.
        // Note that the key is "given_name" which is the OIDC claim for given name
        userAttributes.addAttribute("given_name", createParams.getFirstname() + " " + createParams.getLastname());

        // Adding user's phone number
        userAttributes.addAttribute("phone_number", createParams.getPhone());

        // Adding user's email address
        userAttributes.addAttribute("email", createParams.getPhone());

        SignUpHandler signupCallback = new SignUpHandler() {

            @Override
            public void onSuccess(CognitoUser cognitoUser, boolean userConfirmed, CognitoUserCodeDeliveryDetails cognitoUserCodeDeliveryDetails) {
                // Sign-up was successful

                currentUser = cognitoUser;

                // Check if this user (cognitoUser) needs to be confirmed
                if(!userConfirmed) {
                    // This user must be confirmed and a confirmation code was sent to the user
                    // cognitoUserCodeDeliveryDetails will indicate where the confirmation code was sent
                    // Get the confirmation code from user
                    Timber.d("Sent confirmation code");
                }
                else {
                    // The user has already been confirmed
                    Timber.d("User has already been confirmed.");
                }
            }

            @Override
            public void onFailure(Exception exception) {
                // Sign-up failed, check exception for the cause
            }
        };

        userPool.signUpInBackground(userId, password, userAttributes, null, signupCallback);
    }
}

如何将 onSuccess 或 OnFailure 的结果返回到 RegisterFragment 类?

【问题讨论】:

  • rxjava1 还是 rxjava2?编辑:没关系...Subscription 告诉我这是 rxjava1

标签: android rx-java rx-android


【解决方案1】:

看起来 Cognito SDK 已经提供了一种异步方式来获取信息。为了让您将其包装到 rx 流中,您应该考虑使用 Subject

Subject 既是Observables 能够发出数据,又是Observers 能够接收数据。 Subject 可以等待接收回调数据,获取数据,然后将其发送到流中。

public Observable<CognitoUser> SignUp(CreateParams createParams){
    BehaviorSubject<CognitoUser> subject = BehaviorSubject.create();

    // ...

    SignUpHandler signupCallback = new SignUpHandler() {

        @Override
        public void onSuccess(CognitoUser cognitoUser, boolean userConfirmed, CognitoUserCodeDeliveryDetails cognitoUserCodeDeliveryDetails) {
            // Sign-up was successful

            // Check if this user (cognitoUser) needs to be confirmed
            if(!userConfirmed) {
                // This user must be confirmed and a confirmation code was sent to the user
                // cognitoUserCodeDeliveryDetails will indicate where the confirmation code was sent
                // Get the confirmation code from user
                Timber.d("Sent confirmation code");
            }
            else {
                // The user has already been confirmed
                Timber.d("User has already been confirmed.");
            }

            subject.onNext(cognitoUser);
            subject.onComplete();
        }

        @Override
        public void onFailure(Exception exception) {
            subject.onError(exception);
        }
    };

    userPool.signUpInBackground(userId, password, userAttributes, null, signupCallback);
    return subject;
}

【讨论】:

  • 这个解决方案有个问题:调用这个方法就触发了你的主题,然后把你的主题返回给你的用户。 Observables 应该被subscribe() 延迟触发。在用户订阅之前,什么都不会发生。
  • 这绝对是一个hot observable。应该如何对待 observables 没有对错之分,因为由开发人员决定 hot vs cold。之所以在这里建议BehaviorSubject,正是因为这是一个hot observable。 :)
  • 这样的方法签名:public Observable&lt;CognitoUser&gt; SignUp(CreateParams createParams) 一旦我订阅它就应该被触发,不是吗?确实,主题是 hot 可观察的。即使没有订阅者,他们也可以为所欲为。但通常我们希望我们的网络在订阅时被调用,而不是在我们调用 getObservable()methode 时调用
  • 你的解决方案的场景是这样的:我调用了 SignUp-> 它调用了 signUpInBackground -> 一旦我订阅了它 -> 我得到了 replayed 值。一个正常的情况应该是这样的:我调用了 SignUp -> 我得到了一个 Observable -> 我订阅了它 -> 它调用了 signUpInBackground -> 我得到了 SignUpValue。请注意,您的 replayed 值并不完全是您调用的值,它可能是您调用此方法两次并从它的 replay 值中获取了错误的值。
  • 想象一下你调用你的signUp方法一次,订阅前得到OK结果。无论出于何种原因,您第二次调用它,但您失去了互联网连接。您只能获得两个呼叫的失败结果。
【解决方案2】:

如果您使用的是 RxJava2。您可以使用 create() 运算符创建自己的异步调用:

public class AwsAuthClient {

    public Observable<CognitoUser> SignUp(CreateParams createParams){
        return Observable.create(emitter -> {
            SignUpHandler signupCallback = new SignUpHandler() {

                @Override
                public void onSuccess(CognitoUser cognitoUser, boolean userConfirmed, CognitoUserCodeDeliveryDetails cognitoUserCodeDeliveryDetails) {
                // Sign-up was successful
                emitter.onNext(cognitoUser);
                // Check if this user (cognitoUser) needs to be confirmed
                if(!userConfirmed) {
                // This user must be confirmed and a confirmation code was sent to the user
                // cognitoUserCodeDeliveryDetails will indicate where the confirmation code was sent
                // Get the confirmation code from user
                Timber.d("Sent confirmation code");
                }
                else {
                    // The user has already been confirmed
                    Timber.d("User has already been confirmed.");
                }
                emitter.onComplete();
            }

            @Override
            public void onFailure(Exception exception) {
                // Sign-up failed, check exception for the cause
                emitter.onError(exception);
            }
        };
        //cancel the call
        Observable.setCancellable(//your cancel code)
    })
}

编辑:如果您使用的是 RxJava1(最新版本 1.3.2),您可以只使用 Observable.create(Action1>,BackPressureMode) 而不是 create,它是 安全

        Observable.create(new Action1<Emitter<CognitoUser extends Object>>() {
        @Override
        public void call(Emitter<CognitoUser> emitter) {
            SignUpHandler signupCallback = new SignUpHandler() {
                @Override
                public void onSuccess(CognitoUser cognitoUser, boolean userConfirmed, CognitoUserCodeDeliveryDetails cognitoUserCodeDeliveryDetails) {
                    if (!userConfirmed) {
                        Timber.d("Sent confirmation code");
                    } else {
                        Timber.d("User has already been confirmed.");
                    }
                    emitter.onNext(cognitoUser);

                    emitter.onComplete();
                }

                @Override
                public void onFailure(Exception exception) {
                    emitter.onError(exception);
                }
            };
            emitter.setCancellation(new Cancellable() {
                @Override
                public void cancel() throws Exception {
                    //Your Cancellation
                }
            });
            signUpInBackground(userId, password, userAttributes, null, signupCallback);
        }

        //Because RxJava 1 doesn't have Flowable so you need add backpressure by default.
    }, Emitter.BackpressureMode.NONE );

【讨论】:

  • 但是在 RxJava1 中远离create! RxJava1 中 create 的等价物是什么?
  • @EugenPechanec 是的。如果您使用的是较新版本的 RxJava 1。有一些重载可以安全地为您创建像 RxJava2 这样的 Observable。例如:Observable&lt;T&gt; create(Action1&lt;Emitter&lt;T&gt;&gt; emitter, Emitter.BackpressureMode backpressure)
  • 因为我使用的是 1.X,所以另一个答案最终对我有用,但这有助于了解版本 2。
  • @stackunderflows 有与 RxJava2 在 RxJava 1 中创建的等效运算符。但我真的不记得那是什么了。 fromEmitter/fromAsync 或类似的东西。并且主题也应该谨慎使用。
  • @stackunderflows 并使用该主题解决方案,存在一些问题。请参阅该答案的评论。
猜你喜欢
  • 2019-06-11
  • 2020-04-17
  • 1970-01-01
  • 1970-01-01
  • 2021-12-02
  • 1970-01-01
  • 1970-01-01
  • 2020-05-13
  • 2016-05-08
相关资源
最近更新 更多