使用SynchronousQueue的指导

时间:2012-01-01 13:41:26

标签: android android-asynctask

我需要执行一系列http请求,每个请求都可能依赖于之前的http响应。我已经能够使用AsyncTask“树”来实现这一点,但随着决策树的增长,AsyncTask技术变得更加笨拙。

我认为以某种方式使用SynchronousQueue(或其他类型的队列)是最好的方法,但我似乎找不到任何关于如何将队列用于http请求的良好指导或教程。

任何人都可以提供任何指导或指向任何有关使用SynchronousQueue的好教程或建议最佳类型的队列吗?

2 个答案:

答案 0 :(得分:1)

使用java.util.concurrent.SingleThreadExecutor并从每个HTTP操作和结果处理程序中生成Runnable。您可以在确定是否需要继续进度时向其提交后续任务。

例如,HTTP“任务”将在成功时运行并提交结果“任务”,或者在失败时提交错误“任务”。结果任务将在完成处理后再提交另一个HTTP任务。使用SingleThreadExecutor确保一次只运行一个任务。

如果您可以同时处理多个操作,则可以使用ThreadPoolExecutor

拿出所有这些,并将其包裹在管理顶级“开球”的AsyncTask中,并等待一切完成。使用ConditionVariable或其他东西来同步“结束”信号(使用完成“任务”)可能会很有用,这样您就可以安全地拆除Executor

SynchronousQueue对您没有任何帮助,因为它让您完成所有的胎面管理。如果您使用的Executor已全部处理,您所处理的只有RunnableFuture s。这可能就是为什么你没有找到任何教程。无论如何,Executor都在下面使用其中一个队列实现!

根据要求,这是一些骨架Java代码。不受支持的未经测试的原样。这应该让你开始。如果您不喜欢ConditionVariable,则可以使用其他同步对象。

这是一种通用技术,不是Android特有的,可以在其他环境中自由使用。

这用作状态机,HttpTask等形成状态,并且通过将下一状态提交到ExecutorService来对转换进行硬编码。甚至还有一个“大爆炸,所以每个人都知道何时拍手”的形式为ConditionVariable

有些人可能会认为DoneTaskFailedTask过分,但它会使下一状态机制保持一致,并让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
    }
}
}

答案 1 :(得分:0)

在不知道用例的详细信息的情况下我无法肯定地说,但您可能想要避免使用SynchronousQueue,因为它会阻止线程将事物放入队列,直到侦听器线程将其从队列中取出。如果您正在使用UI线程,那么您将锁定UI。

我认为BlockingQueue可能适合您的需求。 JavaDoc有一个很好的生产者 - 消费者的例子。

相关问题