我想在改造中为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);
}
}
};
}
});
}
在上面的代码中,我检查如果我获得了像Invalid API KEY这样的特定状态代码,那么我调用API来获取新的API密钥,而不是直接将状态提供给原始订阅者。
成功获得新的API密钥后,我会递归调用包装器&amp;尝试给原始订阅者回复。但问题是原始订阅者未获得onNext
回调
我在这里缺少什么?有没有其他方法可以实现我想要做的事情?
答案 0 :(得分:2)
您需要添加一些重试逻辑,以防您收到无效密钥失败,例如
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 */)
答案 1 :(得分:1)
最后,我设法创建了一个包装器,它可以处理常见的Progressbar&amp; amp;无效密钥API响应时重试逻辑。在许多情况下,这种包装器可能很有用。感谢@JohnWowUs的回答,这有助于我理解事物和实现这个包装器。
这是工作代码
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
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.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>>() {
//...
}