Question

How to execure RoboSpice requests synchronously? Is it possible? I would like to use RoboSpice in IntentService.

Edit:

From time to time comes the need to execute something synchronously e.g. in service. In my recent project I have to queue some different types of requests and I wanted to use IntentService with RoboSpice. In my case when I'm executing different requests I need to wait for results from request1 then pass data from it to request2 and execute it.

I want to have some kind of batch request queue. Lets say we have two types of request: request1 and request2. request2 needs data fetched by request1:

  1. execute request1
  2. wait
  3. get data fetched by request1 and pass to request2
  4. execute request2
  5. wait
  6. goto 1.

I wanted to use IntentService (queue) but it dies after starting requests because of async.

In my opinion using listeners or CountDownLatch isn't the best way.

Était-ce utile?

La solution

As proposed by Riccardo Ciovatti on the RoboSpice mailing list, a direct answer to your question is :

final CountDownLatch latch = new CountDownLatch(1);

final YourRequest request = new YourRequest();
spiceManager.execute(request, new RequestListener<YourResponse>() {

    @Override
    public void onRequestFailure(SpiceException spiceException) {
        latch.countDown();
    }

    @Override
    public void onRequestSuccess(YourResponse response) {
        latch.countDown();
    }
});

latch.await();

But this isn't a good idiom to use unless it's being executed on a background thread. Asynchronous processing is an essential feature of RoboSpice; it keeps the UI thread free.

Autres conseils

Using RoboSpice in IntentService. Solved the problem with help of CountDownLatch. Let's say we have 2 different SpiceManagers and some syncMethods to execute in sequence in the IntentService.

Globals:

private final SpiceManager aSpiceManager =
       new SpiceManager(ASpiceService.class);
private final SpiceManager bSpiceManager =
       new SpiceManager(BSpiceService.class);

private CountDownLatch handleIntentLatch;

onHandleIntent: Before we execute syncMethodA - we init our CountDownLatch with 1. After executing syncMethodA we await() for countDown() on our latch. When later some method will call countDown() on our latch at least once - method onHandleIntent will continue it's execution and finish which will trigger IntentService onDestroy() callback.

@Override
protected void onHandleIntent(Intent intent) {
    handleIntentLatch = new CountDownLatch(1);

    syncMethodA();

    try {
        handleIntentLatch.await();
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

syncMethodA(): Suppose we need to launch some sync methods in sequence (syncMethodA which callback executes syncMethodB etc).

private void syncMethodA() {
    SpiceRequest requestA = new SpiceRequest();

    if (!aSpiceManager.isStarted()) {
        LogUtils.LOGD(TAG, "starting aSpiceManager");
        aSpiceManager.start(getApplicationContext());
    }

    aSpiceManager.execute(requestA , new RequestListener<ResponseA>() {
        @Override
        public void onRequestSuccess(final ResponseA responseA) {
            // SOME LOGIC

            syncMethodB();

            // SOME LOGIC

        @Override
        public void onRequestFailure(SpiceException spiceException) {
            handleIntentLatch.countDown();
            // SOME LOGIC
        }
    });
}

syncMethodB, syncMethodC etc are the same - in onRequestSuccess we start next syncMethodX. In on onRequestFailure we countDown() our latch (handleIntentLatch).

Very important!!! In last syncMethodX in sequence (after which completion we want onHandleIntent method to continue it's execution and finish which will result IntentService to stop) - we countDown() our latch in onRequestSuccess ALSO.

onDestroy: here we stop our SpinceManagers.

@Override
public void onDestroy() {
    super.onDestroy();
    LogUtils.LOGD(TAG, "onDestroy");
    shutDownSpiceManagers();
}

shutDownSpiceManagers:

private void shutDownSpiceManagers() {
    if (aSpiceManager.isStarted()) {
        LogUtils.LOGD(TAG, "stopping aSpiceManager");
        aSpiceManager.shouldStop();
    }
    if (bSpiceManager.isStarted()) {
        LogUtils.LOGD(TAG, "stopping bSpiceManager");
        bSpiceManager.shouldStop();
     }
}

All should be OK now: no leaked Context, SpiceManagers will be killed in onDestroy AND ONLY AFTER callbacks are resolved.

If you don't want exactly synchronous execution, but only want requests to be executed sequentially in some order, you may write your own simple class which will just execute request B when request A succeeded or failed. Like that: https://github.com/Deepscorn/Shared/blob/master/app/src/main/java/com/gamelift/shared/request/base/RequestSequenceExecutor.java. So, code will look like:

sequenceExecutor.setRequest(new FirstRequest());
sequenceExecutor.setOnSuccessListener(FirstRequest.class, new OnSuccessListener {
   public void onSuccess() {
     sequenceExecutor.setRequest(new SecondRequest());
   }
};

sequenceExecutor.setOnFaulListener(FirstRequest.class, new OnFailListener {
   public void onFail() {
     sequenceExecutor.setRequest(new OnFirstFailRequest());
   }
};

sequenceExecutor.setOnSuccessListener(SecondRequest.class, new OnSuccessListener {
   public void onSuccess() {
     notifyDone();
     return;
   }
};

sequenceExecutor.setDefaultOnFailListener(new OnFailListener {
   public void onFail(Exception e) {
     notifyDone();
     log(e);
     return;
   }
};
sequenceExecutor.execute() //starts execution
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top