我可以用volley做同步请求吗?

想象一下,我在一个已经有后台线程的服务。 我可以在同一个线程中使用抽签方式做一个请求,以便callback同步发生吗?

有两个原因: – 首先,我不需要另一个线程,创build它将是一个浪费。 – 其次,如果我在ServiceIntent中,线程的执行将在callback之前完成,因此我将不会有任何回应。 我知道我可以创build自己的服务,它有一个我可以控制的runloop的线程,但是最好是有这个function。

谢谢!

看起来像Volley的RequestFuture类是可能的。 例如,要创build同步的JSON HTTP GET请求,可以执行以下操作:

 RequestFuture<JSONObject> future = RequestFuture.newFuture(); JsonObjectRequest request = new JsonObjectRequest(URL, new JSONObject(), future, future); requestQueue.add(request); try { JSONObject response = future.get(); // this will block } catch (InterruptedException e) { // exception handling } catch (ExecutionException e) { // exception handling } 

注意@Matthews的答案是正确的,但是如果你在另一个线程上,而当你没有互联网的时候你会打个电话, 你的错误callback将在主线程中被调用,但是你所在的线程将被阻塞。 (因此,如果该线程是一个IntentService,您将永远不能发送另一个消息,并且您的服务将基本上是死的)。

使用具有超时future.get(30, TimeUnit.SECONDS)get()版本并捕获错误以退出您的线程。

匹配@Mathews回答:

  try { return future.get(30, TimeUnit.SECONDS); } catch (InterruptedException e) { // exception handling } catch (ExecutionException e) { // exception handling } catch (TimeoutException e) { // exception handling } 

下面我把它包装在一个方法中,并使用不同的请求:

  /** * Runs a blocking Volley request * * @param method get/put/post etc * @param url endpoint * @param errorListener handles errors * @return the input stream result or exception: NOTE returns null once the onErrorResponse listener has been called */ public InputStream runInputStreamRequest(int method, String url, Response.ErrorListener errorListener) { RequestFuture<InputStream> future = RequestFuture.newFuture(); InputStreamRequest request = new InputStreamRequest(method, url, future, errorListener); getQueue().add(request); try { return future.get(REQUEST_TIMEOUT, TimeUnit.SECONDS); } catch (InterruptedException e) { Log.e("Retrieve cards api call interrupted.", e); errorListener.onErrorResponse(new VolleyError(e)); } catch (ExecutionException e) { Log.e("Retrieve cards api call failed.", e); errorListener.onErrorResponse(new VolleyError(e)); } catch (TimeoutException e) { Log.e("Retrieve cards api call timed out.", e); errorListener.onErrorResponse(new VolleyError(e)); } return null; } 

可能推荐使用Futures,但是如果出于任何原因你不想要,而不是烹饪自己的同步阻塞的东西,你应该使用java.util.concurrent.CountDownLatch 。 所以这样可以这样工作

 //I'm running this in an instrumentation test, in real life you'd ofc obtain the context differently... final Context context = InstrumentationRegistry.getTargetContext(); final RequestQueue queue = Volley.newRequestQueue(context); final CountDownLatch countDownLatch = new CountDownLatch(1); final Object[] responseHolder = new Object[1]; final StringRequest stringRequest = new StringRequest(Request.Method.GET, "http://google.com", new Response.Listener<String>() { @Override public void onResponse(String response) { responseHolder[0] = response; countDownLatch.countDown(); } }, new Response.ErrorListener() { @Override public void onErrorResponse(VolleyError error) { responseHolder[0] = error; countDownLatch.countDown(); } }); queue.add(stringRequest); try { countDownLatch.await(); } catch (InterruptedException e) { throw new RuntimeException(e); } if (responseHolder[0] instanceof VolleyError) { final VolleyError volleyError = (VolleyError) responseHolder[0]; //TODO: Handle error... } else { final String response = (String) responseHolder[0]; //TODO: Handle response... } 

作为对@Blundells和@Mathews两个答案的补充观察,我不确定任何调用是否通过Volley传递给主线程。

来源

查看RequestQueue实现 ,看起来RequestQueue使用NetworkDispatcher执行请求,并使用ResponseDelivery传递结果( ResponseDelivery注入到NetworkDispatcher )。 ResponseDelivery反过来由主线程(在RequestQueue实现的第112行周围的某处)产生的Handler创build。

NetworkDispatcher实现的第135行的某处,似乎也可以通过与任何错误相同的ResponseDelivery传递成功的结果。 再次; 一个基于主线程产生的HandlerResponseDelivery

合理

对于从IntentService发出请求的用例,假定服务的线程应该阻塞,直到我们有了Volley的响应(保证生存的运行时范围来处理结果)。

build议解决scheme

一种方法是重写创buildRequestQueue的默认方式,其中使用替代构造函数,而不是注入从当前线程而不是主线程产生的ResponseDelivery 。 但是,我没有调查这个影响。

我用锁来实现这个效果,现在我想知道是否正确的我的方式任何人想评论?

 // as a field of the class where i wan't to do the synchronous `volley` call Object mLock = new Object(); // need to have the error and success listeners notifyin final boolean[] finished = {false}; Response.Listener<ArrayList<Integer>> responseListener = new Response.Listener<ArrayList<Integer>>() { @Override public void onResponse(ArrayList<Integer> response) { synchronized (mLock) { System.out.println(); finished[0] = true; mLock.notify(); } } }; Response.ErrorListener errorListener = new Response.ErrorListener() { @Override public void onErrorResponse(VolleyError error) { synchronized (mLock) { System.out.println(); finished[0] = true; System.out.println(); mLock.notify(); } } }; // after adding the Request to the volley queue synchronized (mLock) { try { while(!finished[0]) { mLock.wait(); } } catch (InterruptedException e) { e.printStackTrace(); } } 

我想补充一点马修公认的答案。 虽然RequestFuture似乎从您创build它的线程进行同步调用,但它不。 相反,调用是在后台线程上执行的。

根据我在通过库之后所了解的, RequestQueue中的RequestQueue将在其start()方法中分派:

  public void start() { .... mCacheDispatcher = new CacheDispatcher(...); mCacheDispatcher.start(); .... NetworkDispatcher networkDispatcher = new NetworkDispatcher(...); networkDispatcher.start(); .... } 

现在, CacheDispatcherNetworkDispatcher类都扩展了线程。 所以有效地产生了一个新的工作者线程,用于出队请求队列,并将响应返回给由RequestFuture内部实现的成功和错误侦听器。

虽然你的第二个目的已经达到了,但是你的第一个目的并不是总是产生一个新的线程,不pipe从哪个线程执行RequestFuture

总之, 真正的同步请求是不可能的,默认的Volley库。 纠正我,如果我错了。