简体   繁体   中英

Multiple similar requests in sequence in retrofit

Is there any way to execute multiple requests in sequence in Retrofit?

These requests uses same Java interface and differ only by parameters they take which are contained in ArrayList .

For requests A1, A2, A3, A4, A5...... An

  1. Hit A1,

  2. onResponse() of A1 is called

  3. Hit A2,

  4. onResponse() of A2 is called

  5. Hit A3 .

.

.

.

.

. onResponse() of An is called.

The problem can be easily solved with RxJava .

Assuming you have a retrofit Api class, that returns a Completable :


    interface Api {
      @GET(...)
      fun getUser(id: String): Completable
    }

Then you can perform this:


    // Create a stream, that emits each item from the list, in this case "param1" continued with "param2" and then "param3"
    Observable.fromIterable(listOf("param1", "param2", "param3"))
      // we are converting `Observable` stream into `Completable`
      // also we perform request here: first time parameter `it` is equal to "param1", so a request is being made with "param1"
      // execution will halt here until responce is received. If response is successful, only then a call with second param ("param2") will be executed
      // then the same again with "param3"
      .flatMapCompletable { api.getUser(it) }
      // we want request to happen on a background thread
      .subscribeOn(Schedulers.io())
      // we want to be notified about completition on UI thread
      .observeOn(AndroidSchedulers.mainThread())
      // here we'll get notified, that operation has either successfully performed OR failed for some reason (specified by `Throwable it`)
      .subscribe({ println("completed") }, { println(it.message) })

If your retrofit API does not return a Completable , then change api.getUser(it) to api.getUser(it).toCompletable() .

You can do this easily by using zip function in Rx (For ex: each request in retrofit 2 return Observable<Object> ). It will run request sequentially. You can try my codes below:

public Observable buildCombineObserverable() {
        List<Observable<Object>> observables = new ArrayList<>();
        for (int i = 0; i < number_of_your_request; i++) {
            observables.add(your_each_request_with_retrofit);
        }

        return Observable.zip(observables, new FuncN<Object>() {
            @Override
            public Object call(Object... args) {
                return args;
            }
        });
    }

You can subscribe this Observable and get data from all requests. Zip function will do request sequentially but zip data and return data as Object...

Ok, I don't know about Retrofit, I use loopj's library, but the concept is the same. they both have a method for success and a method for failure. so here is my general suggestion:

    ArrayList<MyRequest> requests = new ArrayList<>();
    int        numberOfRequests = 10;
    JSONObject params           = null;
    try{
        params = new JSONObject("{\"key\":\"value\"}");
    }catch(JSONException e){
        e.printStackTrace();
    }
    MyRequest firstRequest = new MyRequest();
    requests.add(firstRequest);
    for(int i = 0; i < numberOfRequests; i++){
        MyRequest myRequest = new MyRequest();
        requests.get(requests.size() - 1).addNextRequest(myRequest);
        myRequest.addPreviousRequest(requests.get(requests.size() - 1));
        //don't invoke sendRequest before addNextRequest
        requests.get(requests.size() - 1).sendRequest(params, "example.com", App.context);
        requests.add(myRequest);
    }
    requests.get(requests.size() - 1).sendRequest(params, "example.com", App.context);

and the MyRequest class:

import android.content.Context;
import com.loopj.android.http.AsyncHttpClient;
import com.loopj.android.http.AsyncHttpResponseHandler;
import org.json.JSONObject;
import cz.msebera.android.httpclient.Header;
import cz.msebera.android.httpclient.entity.StringEntity;

public class MyRequest{

private Object result, nextRequestsResult;

private MyRequest nextRequest, previousRequest;

public void addNextRequest(MyRequest nextRequest){
    this.nextRequest = nextRequest;
}

public void addPreviousRequest(MyRequest previousRequest){
    this.previousRequest = previousRequest;
}

public void sendRequest(JSONObject parameters, String url, Context ctx){
    AsyncHttpClient mClient     = new AsyncHttpClient();
    StringEntity    entity      = new StringEntity(parameters.toString(), "UTF-8");
    String          contentType = "application/json";
    mClient.post(ctx, url, entity, contentType,
                 new AsyncHttpResponseHandler(){

                     private void sendResult(Object... results){
                         MyRequest.this.result = results;
                         if(previousRequest != null){
                             if(nextRequest != null){
                                 if( nextRequestsResult != null){
                                     previousRequest.onResult(results, nextRequestsResult);
                                 }else{
                                     //next request's result is not ready yet
                                     //so we don't do anything here. When nextRequestsResult
                                     //gets ready, it will invoke this request's onResult
                                 }
                             }else {
                                 //nextRequest == null means this the last request
                                 previousRequest.onResult(results);
                             }
                         }else{
                             //previousRequest == null means this is the first request
                             if(nextRequest != null){
                                 if(nextRequestsResult != null){
                                     previousRequest.onResult(results, nextRequestsResult);
                                 }else{
                                     //next request's result is not ready yet
                                     //so we don't do anything here. When nextRequestsResult
                                     //gets ready, it will invoke this request's onResult
                                 }
                             }else{
                                 //next request and previous request are null so it means
                                 //this is the only request, so this is the final destination
                                 doFinalJobWithResults(results);
                             }
                         }
                     }

                     @Override
                     public void onSuccess(final int statusCode, final Header[] headers,
                                           final byte[] responseBody){
                         sendResult(responseBody, true, null, false);//whatever
                     }

                     @Override
                     public void onFailure(final int statusCode, final Header[] headers,
                                           final byte[] responseBody,
                                           final Throwable error){
                         sendResult(responseBody, error);//or just sendResult();
                     }
                 });
}

/**
 This method should be invoked only by next request

 @param nextRequestsResult
 results of the next request which this request is expecting.
 */
private void onResult(Object... nextRequestsResult){
    this.nextRequestsResult = nextRequestsResult;
    //do whatever you want with the result of next requests here
    if(previousRequest != null){
        if(result != null){
            previousRequest.onResult(result, this.nextRequestsResult);
        }
    }else{
        //if it doesn't have previous request then it means this is the first request
        //so since this method gets invoked only by next request then it means
        //all of the next requests have done their job and this is the final destination
        if(nextRequestsResult != null){
            if(this.result != null){
                doFinalJobWithResults(nextRequestsResult, this.result);
            }
        }
    }
}

private void doFinalJobWithResults(Object... results){
    //whatever
}
}

It's a general purpose class, you can send hundreds of network requests simultaneously but their results will be processed in sequence. This way for example 100 requests will be sent to the server but it takes the time of one request to get all responses of them and process.

I haven't tested this code at all, it may have some bugs and mistakes, I wrote it just for this question only to give an idea.

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