我需要执行一系列http请求,每个请求都可能依赖于之前的http响应。我已经能够使用AsyncTask“树”来实现这一点,但随着决策树的增长,AsyncTask技术变得更加笨拙。
我认为以某种方式使用SynchronousQueue(或其他类型的队列)是最好的方法,但我似乎找不到任何关于如何将队列用于http请求的良好指导或教程。
任何人都可以提供任何指导或指向任何有关使用SynchronousQueue的好教程或建议最佳类型的队列吗?
答案 0 :(得分:1)
使用java.util.concurrent.SingleThreadExecutor
并从每个HTTP操作和结果处理程序中生成Runnable
。您可以在确定是否需要继续进度时向其提交后续任务。
例如,HTTP“任务”将在成功时运行并提交结果“任务”,或者在失败时提交错误“任务”。结果任务将在完成处理后再提交另一个HTTP任务。使用SingleThreadExecutor
确保一次只运行一个任务。
如果您可以同时处理多个操作,则可以使用ThreadPoolExecutor
。
拿出所有这些,并将其包裹在管理顶级“开球”的AsyncTask
中,并等待一切完成。使用ConditionVariable
或其他东西来同步“结束”信号(使用完成“任务”)可能会很有用,这样您就可以安全地拆除Executor
。
SynchronousQueue
对您没有任何帮助,因为它让您完成所有的胎面管理。如果您使用的Executor
已全部处理,您所处理的只有Runnable
和Future
s。这可能就是为什么你没有找到任何教程。无论如何,Executor
都在下面使用其中一个队列实现!
根据要求,这是一些骨架Java代码。不受支持的未经测试的原样。这应该让你开始。如果您不喜欢ConditionVariable
,则可以使用其他同步对象。
这是一种通用技术,不是Android特有的,可以在其他环境中自由使用。
这用作状态机,HttpTask
等形成状态,并且通过将下一状态提交到ExecutorService
来对转换进行硬编码。甚至还有一个“大爆炸,所以每个人都知道何时拍手”的形式为ConditionVariable
。
有些人可能会认为DoneTask
和FailedTask
过分,但它会使下一状态机制保持一致,并让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有一个很好的生产者 - 消费者的例子。