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

Loc*_*ike 130 java android android-volley

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

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

谢谢!

Mat*_*att 178

看起来有可能与Volley一起 RequestFuture课程.例如,要创建同步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
}
Run Code Online (Sandbox Code Playgroud)

  • 它永远是阻挡! (21认同)
  • 提示:如果在将请求添加到请求队列之前调用future.get(),它可能会永远阻塞. (9认同)
  • @tasomaniac更新.这使用`JsonObjectRequest(String url,JSONObject jsonRequest,Listener <JSONObject> listener,ErrorListener errorlistener)`构造函数.`RequestFuture <JSONObject>`实现了`Listener <JSONObject>`和`ErrorListener`接口,因此它可以用作最后两个参数. (5认同)
  • 应该说你不应该在主线程上这样做.这对我来说并不清楚.如果主线程被`future.get()`阻塞,那么应用程序将停止或进入超时以确定是否设置为. (4认同)
  • 它将永远被阻止,因为你可能有连接错误阅读Blundell答案 (3认同)

Blu*_*ell 122

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

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

要匹配@Mathews答案:

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

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

   /**
     * 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;
    }
Run Code Online (Sandbox Code Playgroud)


Tim*_*imo 8

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

由于人们似乎真的试图这样做并遇到一些麻烦,我决定我实际上提供了一个"现实生活"的工作样本.这是https://github.com/timolehto/SynchronousVolleySample

现在即使解决方案有效,它也有一些局限性.最重要的是,您无法在主UI线程上调用它.Volley确实在后台执行请求,但默认情况下,Volley使用Looper应用程序的主要部分来发送响应.这会导致死锁,因为主UI线程正在等待响应,但是在处理传递之前Looper等待onCreate完成.如果你真的想要这样做,你可以,而不是静态帮助器方法,实例化你自己RequestQueue传递它自己ExecutorDelivery绑定到Handler使用一个Looper绑定到主UI线程不同的线程.