Le_Enot
Le_Enot

Reputation: 839

Multiple asynchronous request handling in java

I'm writing android app, which uses some site api. This site provides sort of SDK to handle this api. I'm using it to make some requests to the site.
So, to make a request I should do like this:

VKRequestListener listener = new VKRequestListener(){
    @Override
    public void onComplete(VKResponse response){
      // do smth when response is complete
    }
}
VKRequest vkRequest = new VKRequest("request", "parameters");
vkRequest.executeWithListener(listener);

This listener runs asynchronously, so if I make single request, all goes fine: I can handle it inside onComplete method. But what to do if I need to make multiple requests, furthermore, each next request depends on previous response? I only see two ways, both look awful:
1. Make each next request in the onComplete method. This seems to be too nesting and inflexible practice.
2. Make some kind of synchronous operation: after main thread executes request, check somehow if onComplete executed (btw, how? with some kind of flag objects?) and if not, do Thread.sleep(1000), repeat this until response returned, than make another response.
So, tell me please if there are 'normal' way to handle multiple async requests?

Upvotes: 0

Views: 3326

Answers (3)

Le_Enot
Le_Enot

Reputation: 839

I was thinking about simple solution a lot, and I've done this:

class RequestFlags{
    int requestCount;
    Params params;
};
private final RequestFlags requestFlags;

public requestWrapper(RequestFlags rf){
    VKRequestListener listener = new VKRequestListener(){
        @Override
        public void onComplete(VKResponse response){
        // do smth when response is complete
             if(requestFlags.requestCount != 0) {
                 requestFlags.requestCount--;
                 requestFlags.params = doSmth(response);
                 requestWrapper(requestFlags);
             } else{
                 // we finished - do smth on finish
             }
        }
    }
    Params someParams;
    switch(requestFlags.params){
        case param1: someParams = doSmthElse();
        break;
        case param2: someParams = null;
        break;
        // etc, etc...
    }
    VKRequest vkRequest = new VKRequest("request", someParams);
    vkRequest.executeWithListener(listener);        
}

So this request wrapper calls itself recursively until all requests handled, coherently and carefully... Hope this helps somebody.

Upvotes: 0

TWiStErRob
TWiStErRob

Reputation: 46480

Multiple Async is really ugly in java, too much boilerplate. Below is how you can hand-roll what you need. But I suggest researching a library as well.

Solution for #1

There's no nesting here (at least not very visible) if you explode your code well: You can even start in the middle of the chain if you already have the necessary data. Esentially you can make your own async interface(s) based on VK classes, but VK totally hidden.

class MyRequests {
    interface GetStuffResult {
        void done(String result, float result2);
        // you can even put other callbacks here to help make decisions in the chain
        // based on UI element states for example
    }

    static void startGetStuff1(String param1, String param2, final GetStuffResult result) {
        VKRequestListener listener = new VKRequestListener(){
            @Override
            public void onComplete(VKResponse response){
                // extract parameters for next request 
                String newParameter1 = ...;
                int newParameter2 = ...;
                startGetStuff2(newParameter1, newParameter2, result);
            }
        };
        VKRequest vkRequest = new VKRequest("request", buildParameters1(param1, param2));
        vkRequest.executeWithListener(listener);
    }

    static void startGetStuff2(String param1, int param2, final GetStuffResult result) {
        VKRequestListener listener = new VKRequestListener(){
            @Override
            public void onComplete(VKResponse response){
                // extract parameters for next request 
                startGetStuff3(newParameter1, newParameter2, newParameter3, result);
            }
        };
        VKRequest vkRequest = new VKRequest("request", buildParameters2(param1, param2));
        vkRequest.executeWithListener(listener);
    }

    static void startGetStuff3(String param1, int param2, Date param3, final GetStuffResult result) {
        VKRequestListener listener = new VKRequestListener() {
            @Override
            public void onComplete(VKResponse response) {
                // extract result
                result.done(resultingString, resultingFloat);
            }
        };
        VKRequest vkRequest = new VKRequest("request", buildParameters3(param1, param2, param3));
        vkRequest.executeWithListener(listener);
    }
}

Solution for #2

To make an async request sync you can do this:
ONLY ever do this on a background thread! And also note that AsyncTask executes tasks 1 by 1 so if you put this in an AsyncTask it'll block other async tasks from executing!

final AtomicReference<VKResponse> result = new AtomicReference<>();
final CountDownLatch latch = new CountDownLatch(1);
VKRequestListener listener = new VKRequestListener(){
    @Override
    public void onComplete(VKResponse response){
        result.set(response);
        latch.countDown();
    }
};
VKRequest vkRequest = new VKRequest("request", "parameters");
vkRequest.executeWithListener(listener);    
// call countDown() even if the request fails if you have an error callback as well!
// and check the existence of response/flag you set if it failed

try {
    latch.await();
} catch (InterruptedException e) {
    Thread.currentThread().interrupt();
}
VKResponse response = result.get(); // will be null if interrupted
// do smth when response is complete

See this about interrupts!

Upvotes: 2

ebarrenechea
ebarrenechea

Reputation: 3785

You should give RxJava, a reactive extensions implementation in java a try. It allows composition of asynchronous calls in a very simple way through observables. There's also an android extension, RxAndroid that gives you access to an android scheduler and activity or fragment lifecycle aware observables.

There are few tutorials and write-ups about using Rx with android, but you should checkout out Dan Lew's one as it covers all the things you'll need. Link here: Grokking RxJava

Upvotes: 1

Related Questions