Search code examples
androidrx-javaretrofitrx-androidokhttp

Avoid OVER_QUERY_LIMIT with rxjava


I see a issue when use Place API with rxjava.

When fetching data so fast, api return "status" : "OVER_QUERY_LIMIT". And I want repeat to get data with specific delay time to avoid it. How to do it with rxjava operator.

getCompositeDisposable().add(
    getDataManager().getAtmsByBankName(nameSearch, location, radius, apiKey)
    .subscribeOn(getSchedulerProvider().io())
    .observeOn(getSchedulerProvider().ui())
    .subscribe(atmResponse -> {
        if (atmResponse.getStatus().equals("OK")) {
            if (atmResponse.getAtmList() != null) {
                atmListLiveData.setValue(atmResponse.getAtmList());
            }
        }
        Log.e("LOG", "inside onNext");
     }, throwable -> {
         getNavigator().handleError(throwable);
         Log.e("LOG", "inside onError");
     }));

This is log with okhttp3:

D/OkHttp: {
   "error_message" : "You have exceeded your daily request quota for this API.",
   "html_attributions" : [],
   "results" : [],
   "status" : "OVER_QUERY_LIMIT"
}
<-- END HTTP (166-byte body)

I use Retrofit for http.

public Observable<ATMResponse> getAtmsByBankName(String nameSearch, String location, String radius, String apiKey) {
    ApiInterface apiInterface = retrofit.create(ApiInterface.class);
    //return apiInterface.getAtms(nameSearch, location, radius, apiKey);
    return apiInterface.getAtms(nameSearch, location, radius, apiKey);
}

Solution

  • You can use the retryWhen() operator to resubscribe to your source when an error occurs. Since the response from your source is a normal item, you have to convert it to an error condition:

    .doOnNext( atmResponse -> {if ( !atmResponse.getStatus().equals("OK") ) {
                                 throw new IllegalStateException();
                              })
    

    (then add any further tests to make sure you have the right type of error) Once it becomes an error, you then use retryWhen() to retry:

    .retryWhen( throwable -> throwable.flatMap( t -> Observable.timer( 1, TimeUnit.SECONDS ) ) )
    

    retryWhen() will emit the throwable that it caught and determine its response based on the result. If a value is emitted, then it will resubscribe to the source; if onComplete() or onError() are emitted, the subscription will finish normally. Using flatMap() allows you to adjust your reaction based on the type of error.