为api调用改装RxAndroid make包装器

为api调用改装RxAndroid make包装器,android,rx-java,retrofit2,rx-android,Android,Rx Java,Retrofit2,Rx Android,我想为改造中的api调用创建一个包装器,这样我就可以在公共位置显示ProgressDialog并处理公共响应 我通过创建这样的包装器实现了这一点 public static <T> Observable<T> callApiWrapper(final Context context, final boolean shouldShowProgress,

我想为改造中的api调用创建一个包装器,这样我就可以在公共位置显示ProgressDialog并处理公共响应

我通过创建这样的包装器实现了这一点

public static <T> Observable<T> callApiWrapper(final Context context,
                                                   final boolean shouldShowProgress,
                                                   final String message,
                                                   final Observable<T> source) {

        final ProgressDialog progressDialog = new ProgressDialog(context);
        if (shouldShowProgress) {
            if (!TextUtils.isEmpty(message))
                progressDialog.setMessage(message);
            else
                progressDialog.setMessage(context.getString(R.string.please_wait));
        }


        return source.lift(new Observable.Operator<T, T>() {
            @Override
            public Subscriber<? super T> call(final Subscriber<? super T> child) {
                return new Subscriber<T>() {
                    @Override
                    public void onStart() {
                        super.onStart();

                        if (shouldShowProgress) {
                            new Handler(Looper.getMainLooper()).post(new Runnable() {
                                @Override
                                public void run() {
                                    progressDialog.show();
                                }
                            });
                        }

                        child.onStart();
                    }

                    @Override
                    public void onCompleted() {
                        if (shouldShowProgress && progressDialog.isShowing())
                            progressDialog.dismiss();
                        child.onCompleted();
                    }

                    @Override
                    public void onError(Throwable e) {
                        if (shouldShowProgress && progressDialog.isShowing())
                            progressDialog.dismiss();
                        child.onError(e);
                    }

                    @Override
                    public void onNext(T t) {

                        /*
                            Handle Invalid API response
                         */

                        if (((BaseResponse) t).getStatus() == RestParams.Codes.INVALID_API_KEY) {
                            mCommonDataModel.setApiKey("");
                            getCommonApiService().getApiKey()
                                    .subscribeOn(Schedulers.newThread())
                                    .observeOn(AndroidSchedulers.mainThread())
                                    .subscribe(new Subscriber<ResponseBody>() {
                                        @Override
                                        public void onCompleted() {

                                        }

                                        @Override
                                        public void onError(Throwable e) {

                                        }

                                        @Override
                                        public void onNext(ResponseBody responseBody) {

                                            try {
                                                String response = responseBody.string();
                                                JSONObject jsonObject = new JSONObject(response);
                                                String key = jsonObject.optString("KEY");

                                                if (!TextUtils.isEmpty(key))
                                                    mCommonDataModel.setApiKey(key);

                                                callApiWrapper(context, shouldShowProgress,
                                                        message, source)
                                                        .subscribeOn(Schedulers.newThread())
                                                        .observeOn(AndroidSchedulers.mainThread())
                                                        .subscribe();
                                            } catch (Exception e) {
                                                e.printStackTrace();
                                            }
                                        }
                                    });
                        } else {
                            if (shouldShowProgress && progressDialog.isShowing())
                                progressDialog.dismiss();


                            child.onNext(t);
                        }

                    }
                };
            }
        });

    }
在上面的代码中,我检查如果我得到了特定的状态代码,比如无效的API密钥,那么我调用了一个API来获得新的API密钥,而不是直接将状态提供给原始订阅者

一旦我成功地获得新的API密钥,我就递归地调用包装器&尝试向原始订户提供响应。但问题是原始订阅者没有得到onNext回调


我错过了什么?有没有其他方法可以实现我的目标?

如果出现无效密钥故障,您需要添加一些重试逻辑,例如

source.flatMap(
    t -> 
    {
        if (((BaseResponse) t).getStatus() == RestParams.Codes.INVALID_API_KEY) {
            return Observable.error(new InvalidKeyException("The key is not valid"));
        }
        else {
            return Observable.just(t);
        }
    }
)
.retryWhen(
    errors -> 
        errors.flatMap(error -> {  

            if (error instanceof InvalidKeyException()) {
                return getCommonApiService().getApiKey()                                         
                                            .flatMap(
                                                responseBody -> {
                                                    String response = responseBody.string();
                                                    JSONObject jsonObject = new JSONObject(response);
                                                    String key = jsonObject.optString("KEY");

                                                    if (TextUtils.isEmpty(key))
                                                        return Observable.error();
                                                else {
                                                    return Observable.just(key);
                                                }})
                                            .doOnNext( key -> mCommonDataModel.setApiKey(key));
            }
            // For anything else, don't retry
            return Observable.error(error);
        }))
.subscribe(/* do what you need to do with the results*/)        
modifiedSource.doOnSubscribe(/* progress bar show logic */)
              .doOnTerminate(/* progress bar dismiss logic */)
为了添加副作用,即在启动订阅时启用进度条,并在完成以下操作时将其关闭:

source.flatMap(
    t -> 
    {
        if (((BaseResponse) t).getStatus() == RestParams.Codes.INVALID_API_KEY) {
            return Observable.error(new InvalidKeyException("The key is not valid"));
        }
        else {
            return Observable.just(t);
        }
    }
)
.retryWhen(
    errors -> 
        errors.flatMap(error -> {  

            if (error instanceof InvalidKeyException()) {
                return getCommonApiService().getApiKey()                                         
                                            .flatMap(
                                                responseBody -> {
                                                    String response = responseBody.string();
                                                    JSONObject jsonObject = new JSONObject(response);
                                                    String key = jsonObject.optString("KEY");

                                                    if (TextUtils.isEmpty(key))
                                                        return Observable.error();
                                                else {
                                                    return Observable.just(key);
                                                }})
                                            .doOnNext( key -> mCommonDataModel.setApiKey(key));
            }
            // For anything else, don't retry
            return Observable.error(error);
        }))
.subscribe(/* do what you need to do with the results*/)        
modifiedSource.doOnSubscribe(/* progress bar show logic */)
              .doOnTerminate(/* progress bar dismiss logic */)

最后,我设法创建了一个包装器,在密钥API响应无效的情况下为我处理公共Progressbar&retry逻辑。在许多情况下,这种包装器可能很有用。感谢@johnowus的回答,帮助我理解并实现了这个包装器

这是工作代码

private static final int MAX_RETRIES = 2;
private static int sCurrentRetryAttempt = 0;

/**
     * Common Wrapper for calling API.
     *
     * @param context            context for showing progress dialog
     * @param shouldShowProgress boolean which indicated if progress dialog should be shown or not
     * @param message            message to be shown in progress dialog. if null passed, then "Please wait..." will be shown
     * @param source             original observable
     * @return observable to which observer can subscribe
     */
    public static <T> Observable<T> callApiWrapper(final Context context,
                                                      final boolean shouldShowProgress,
                                                      final String message,
                                                      final Observable<T> source) {

        // Progress Dialog
        final ProgressDialog progressDialog = setupProgressDialog(context, shouldShowProgress, message);
        if (progressDialog != null) progressDialog.show();

        return source
                .flatMap(new Func1<T, Observable<T>>() {
                    @Override
                    public Observable<T> call(T t) {

                        /*
                         * Check if the response contains invalid key status code.
                         */

                        if (t instanceof BaseResponse) {
                            if (((BaseResponse) t).getStatus() == RestParams.Codes.INVALID_API_KEY) {
                                return Observable.error(new InvalidKeyException("Invalid key"));
                            }
                        }

                        /*
                         * We are here, that means, there wasn't invalid key status code.
                         * So we wouldn't like to handle it so just return to original subscriber
                         */
                        if (progressDialog != null && progressDialog.isShowing())
                            progressDialog.dismiss();

                        return Observable.just(t);
                    }
                }).retryWhen(new Func1<Observable<? extends Throwable>, Observable<?>>() {
                    @Override
                    public Observable<?> call(Observable<? extends Throwable> observable) {

                        return observable.flatMap(new Func1<Throwable, Observable<?>>() {
                            @Override
                            public Observable<?> call(final Throwable throwable) {
                                if (throwable instanceof InvalidKeyException) {
                                    /*
                                     * Check for retry limit. if we already have retried enough, then
                                     * we should tell the original subscriber about the error as it
                                     * doesn't seems recoverable.
                                     */
                                    if (sCurrentRetryAttempt >= MAX_RETRIES) {
                                        if (progressDialog != null && progressDialog.isShowing())
                                            progressDialog.dismiss();

                                        //Resetting the attempts to 0
                                        sCurrentRetryAttempt = 0;
                                        return Observable.error(throwable);
                                    }

                                    //Increase the attempt counter
                                    sCurrentRetryAttempt += 1;
                                    return getCommonApiService().getApiKey()
                                            .subscribeOn(Schedulers.newThread())
                                            .observeOn(AndroidSchedulers.mainThread())
                                            .flatMap(new Func1<ResponseBody, Observable<?>>() {
                                                @Override
                                                public Observable<?> call(ResponseBody responseBody) {
                                                    try {
                                                        /*
                                                         * Check if we succeed in our attempt to handle
                                                         * invalid key
                                                         */
                                                        if (processApiKey(responseBody)) {

                                                            /*
                                                             * We succeeded in our attempts to handle
                                                             * invalid api key, so we will return the
                                                             * original subscriber what it wanted.
                                                             */
                                                            return callApiWrapper(context,
                                                                    shouldShowProgress, message, source);
                                                        } else
                                                            return Observable.just(throwable);
                                                    } catch (Exception e) {
                                                        /*
                                                         * We are here that means something went wrong,
                                                         * so we will retry silently.
                                                         */
                                                        return Observable.just(throwable);
                                                    }
                                                }
                                            });
                                } else {
                                    /*
                                     * For any other error, we are not going to handle right now,
                                     * so just return
                                     */
                                    return Observable.error(throwable);
                                }

                            }
                        });
                    }
                });
    }
&使用此选项与正常选项相同,如:

RestClient.callApiWrapper(mContext, true, null,
                RestClient.getAuthenticationApiService().socialLogIn(name, email, singInVia, mobile, "android", deviceToken))
                .subscribeOn(Schedulers.newThread())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Subscriber<BaseResponse<RegistrationResponse>>() {
//...
}

看起来你有两个不同的问题。一个是您的副作用,另一个是如果第一个API密钥无效,则获取新的API密钥。这两种方法都不需要包装器。它可能无效,但我无法控制API的开发方式,因此要求我以这种方式处理它。还有一些其他的验证,在这些验证中,一个通用的包装器是有意义的,而不是在每个API响应中重复验证。关于副作用,我不明白。你能解释一下怎样才能做得更好吗。谢谢:是的,我可以这样做,但因为任何API都可能得到无效密钥异常,所以用这种方式,我将不得不到处重复编写代码。这就是为什么我要创建一个包装器。