我可以用截击做同步请求吗?

问题

想象一下,我在一个已经有后台线程的服务中。我可以在同一个线程中使用齐射进行请求,以便回调同步发生吗?

这有两个原因: - 首先,我不需要另一个线程,创建它是一种浪费。 - 其次,如果我在ServiceIntent中,线程的执行将在回调之前完成,因此我将没有来自Volley的响应。我知道我可以创建自己的服务,它有一些我可以控制的runloop的线程,但是需要在volley中使用这个功能。

谢谢!


#1 热门回答(169 赞)

Volley'sRequestFutureclass看起来很有可能。例如,要创建同步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
}

#2 热门回答(105 赞)

注意@Matthews答案是正确的但是如果你在另一个线程并且在没有互联网时你进行了一次凌空调用,将在主线程上调用你的错误回调,但你所在的线程将被永久阻止。(因此,如果该线程是IntentService,你将永远无法向其发送另一条消息,你的服务将基本上死亡)。

使用具有timeoutfuture.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;
    }

#3 热门回答(5 赞)

可能建议使用Futures,但如果出于任何原因你不想做,而不是烹饪你自己的同步阻塞的东西,你应该使用ajava.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...
}