Используйте java.util.concurrent.SingleThreadExecutor
и сделайте Runnable
из каждой HTTP-операции и обработчика результатов. Вы можете отправлять в него последующие задачи, определяя, нужно ли вам продолжать выполнение.
Например, HTTP «задача» будет запускаться и отправлять результат «задача» в случае успеха или ошибку «задача» в случае ошибки. Задача Result, в свою очередь, отправит другую задачу HTTP после завершения обработки. Использование SingleThreadExecutor
обеспечивает одновременное выполнение только одной задачи.
Вы можете использовать ThreadPoolExecutor
, если вы можете обрабатывать сразу несколько операций в полете.
Возьмите все это и оберните его в AsyncTask
, который управляет "стартовым" уровнем верхнего уровня и ждет, пока все завершится Вероятно, было бы полезно иметь ConditionVariable
или что-то еще, чтобы синхронизировать сигнал «конец» (используя «задание» «Готово»), чтобы вы могли смело разрушать Executor
.
A SynchronousQueue
не делает здесь ничего полезного, потому что оставляет вам все управление протектором. Если вы используете Executor
, который все обрабатывается, и все, с чем вы имеете дело, это Runnable
s и Future
s. Вероятно, поэтому вы не найдете никаких учебных пособий. В любом случае, все Executor
используют одну из этих реализаций очереди!
В соответствии с запросом приведен скелетный Java-код. Неподдерживаемый непроверенный как есть. Это должно начать вас. Вы можете использовать другой объект синхронизации, если вам не нравится ConditionVariable
.
Это общая методика, не специфичная для Android, не стесняйтесь использовать ее в других контекстах.
Это функционирует как конечный автомат, с HttpTask
и другими, формирующими состояния, а переходы жестко закодированы путем отправки следующего состояния в ExecutorService
. Есть даже «Большой взрыв в конце, так что все знают, когда хлопать» в виде ConditionVariable
.
Некоторые могут считать DoneTask
и FailedTask
излишним, но он поддерживает согласованность механизма Next State и позволяет Future<? extends ResultTask>
функционировать как довольно безопасный для типов контейнер для результатов, и, конечно, удерживает вас от неправильного назначения это.
abstract class BasicTask {
final ExecutorService es;
final ConditionVariable cv;
public BasicTask(ExecutorService es, ConditionVariable cv) {
this.es = es;
this.cv = cv;
}
}
abstract class HttpTask extends BasicTask {
// source omitted.
// you should make a class to prepare e.g. Apache HTTP resources for specific tasks (see below).
}
abstract class ResultTask implements Runnable {
final ConditionVariable cv;
public ResultTask(ConditionVariable cv) {
this.cv = cv;
}
public void run() {
cv.open();
}
}
final class FailedTask extends ResultTask {
final Exception ex;
public FailedTask(ConditionVariable cv, Exception ex) {
super(cv);
this.ex = ex;
}
public Exception getError() { return ex; }
}
final class DoneTask<T> extends ResultTask {
final T results;
public DoneTask(ConditionVariable cv, T results) {
super(cv);
this.results = results;
}
public T getResults() { return results; }
}
class HttpSequence extends AsyncTask<Void,Void,Object> {
// this will capture the ending task
Future<? extends ResultTask> result;
// this is an inner class, in order to set Result. Refactor so these are small.
// if you don't like inner classes, you still need to arrange for capturing the "answer"
final class SomeHttpTask extends HttpTask implements Runnable {
public void run() {
try {
final SomeType thisStep = doTheStuff(lastStep);
if(thisStep.isDone()) {
// we are done here
result = es.submit(new DoneTask<SomeType>(cv, thisStep));
}
else if(thisStep.isFailed()) {
// not done: we can't proceed because of something in the response
throw thisStep.getError();
}
else {
// not done, everything is ok for next step
es.submit(new NextHttpTask(es, cv, thisStep));
}
}
catch(Exception ex) {
result = es.submit(new FailedTask(cv, ex));
}
}
}
final class TheFirstTask extends HttpTask implements Runnable {
// source omitted. just emphasizing you need one of these for each "step".
// if you don't need to set Result, this could be a static inner class.
}
@Override
public Object doInBackground(Void...) {
final ExecutorService es = Executors.newSingleThreadExecutor();
final ConditionVariable cv = new ConditionVariable(false);
try {
es.submit(new TheFirstTask(es, cv));
// you can choose not to timeout at this level and simply block until something happens...
final boolean done = cv.block(timeout);
if(!done) {
// you will need to account for unfinished threads, see finally section!
return new IllegalStateException("timed out waiting on completion!");
}
if(result != null) {
final ResultTask done = result.get();
if(done instanceof DoneTask) {
// pass SomeType to onPostExecute()
return ((DoneTask<SomeTYpe>)done).getResults();
}
else if(done instanceof FailedTask) {
// pass Exception to onPostExecute()
return ((FailedTask)done).getError();
}
else {
// something bad happened, pass it to onPostExecute()
return new IllegalStateException("something unexpected signalled CV!");
}
}
else {
// something bad happened, pass it to onPostExecute()
return new IllegalStateException("something signalled CV without setting result!");
}
}
catch(Exception ex) {
// something outside workflow failed, pass it to onPostExecute()
return ex;
}
finally {
// naive shutdown (doesn't interrupt running tasks): read JavaDoc on ExecutorService for details
es.shutdown();
}
}
@Override
public void onPostExecute(Object result) {
if(result instanceof SomeType) {
// success UI
}
else if(result instanceof Exception) {
// error UI
}
}
}