【问题标题】:Volley - http request in blocking wayVolley - 阻塞方式的http请求
【发布时间】:2013-07-12 06:28:18
【问题描述】:

这些天我正在学习如何使用 Google Volley。快速联网非常方便。似乎所有请求都在 Volley 的后台运行。例如:

volleyRequestQueue.add(new JsonObjectRequest(Method.POST, SIGNUP_URL, reqBody, new SignUpResponseListener(), new MyErrorListener()));

使用上面的代码,我们可以进行一个后台运行的 POST 调用(非阻塞方式)。现在我的问题是: 是否可以以阻塞方式进行 POST 调用?为什么我需要一种阻塞方式来进行 REST 调用?因为某些调用(例如登录)应该在执行其他操作之前完成。

谢谢

【问题讨论】:

标签: android http post android-volley


【解决方案1】:

Volley 支持通过 RequestFutures 阻塞请求。您创建一个普通请求,但将其回调设置为您的请求未来,这只是 volley 对标准 java 期货的扩展。对 future.get() 的调用将被阻塞。

看起来像这样

RequestFuture<JSONObject> future = RequestFuture.newFuture();
JsonObjectRequest request = new JsonObjectRequest(Method.POST, SIGNUP_URL, reqBody, future, future)
volleyRequestQueue.add(request);

try {
    JSONObject response = future.get();
} catch (InterruptedException e) {
} catch (ExecutionException e) {
}

【讨论】:

  • 既然我们将RequestFuture 传递给JsonObjectRequest 构造函数来代替成功和错误侦听器,这是否意味着当我们future.get() 时我们必须手动区分成功和错误?
  • 没错,它在失败时抛出 ExecutionException,您可以从异常中拉出 volley networkRequest 对象以检查状态代码/请求正文。
  • 正如@John Cummings 提到的,你绝对应该阅读stackoverflow.com/questions/16904741/…的讨论
  • InterruptedException 应该通过等待future.get() 直到收到响应来处理。为了清楚起见,我将编辑答案。
  • @Gabriel 我们如何从 ExecutionException 对象中提取 volley networkResponse/status 代码?
【解决方案2】:

这是一个更清晰的答案,可以正确处理InterruptedException 以及超时。请注意,您唯一想要吞下中断并继续的情况是您特别打算使用中断来取消对请求的响应。

RequestFuture<JSONObject> future = RequestFuture.newFuture();
JsonObjectRequest request = new JsonObjectRequest(Method.POST, SIGNUP_URL, reqBody, future, future);
volleyRequestQueue.add(request);

try {
    JSONObject response = null;
    while (response == null) {
        try {
            response = future.get(30, TimeUnit.SECONDS); // Block thread, waiting for response, timeout after 30 seconds
        } catch (InterruptedException e) {
            // Received interrupt signal, but still don't have response
            // Restore thread's interrupted status to use higher up on the call stack
            Thread.currentThread().interrupt();
            // Continue waiting for response (unless you specifically intend to use the interrupt to cancel your request)
        }
    }
    // Do something with response, i.e.
    new SignUpResponseListener().onResponse(response);
} catch (ExecutionException e) {
    // Do something with error, i.e.
    new MyErrorListener().onErrorResponse(new VolleyError(e));
} catch (TimeoutException e) {
    // Do something with timeout, i.e.
    new MyErrorListener().onErrorResponse(new VolleyError(e));
}

【讨论】:

    【解决方案3】:

    如果您想在 Volley 请求之后执行某些操作,请使用回调监听器 onSuccess(在这种情况下为 SignUpResponseListener)并将代码放在那里。这是最佳做法。

    【讨论】:

    • 请解释否决票。我完全知道用户要求阻塞调用,但推理只是“之后执行其他代码。”,所以我的建议仍然是他要求的有效解决方案。
    • 这可能被否决了,因为 1) 它没有回答问题,并且 2) API 允许它使用期货。
    【解决方案4】:

    我无法让 RequestFuture 工作,所以我只使用了 Makibo 建议的回调监听器。我不知道他为什么被否决,这可能是解决不同 Volley 请求的原始常见问题的最佳解决方案,这些请求都取决于初始登录或其他内容。在这个例子中,我想上传一张照片,但首先我要检查用户是否登录。如果没有,我必须登录,然后在上传照片之前等待成功。如果已经登录,直接上传照片即可。

    这是我的示例代码:

    // interface we'll use for listener
    public interface OnLoginListener {
        public void onLogin();
    }
    
    public void uploadPhoto(final String username, final String password, final String photo_location) {
        // first setup callback listener that will be called if/when user is logged in
        OnLoginListener onLoginListener=new OnLoginListener() {
            @Override
            public void onLogin() {
                uploadPhotoLoggedIn(photo_location);
            }
        };
        // simplistic already logged in check for this example, just checking if username is null
        if (loggedInUsername==null) {
            // if it null, login and pass listener
            httpLogin(username, password, onLoginListener);
        } else {
            // if not null, already logged in so just call listener method
            onLoginListener.onLogin();
        }
    }
    
    public void httpLogin(String username, String password, final OnLoginListener onLoginListener) {
        StringRequest loginRequest = new StringRequest(Request.Method.POST, "https://www.example.com/login.php", new Response.Listener<String>() { 
            @Override
            public void onResponse(String txtResponse) {
                Log.d("STACKOVERFLOW",txtResponse);
                // call method of listener after login is successful. so uploadPhotoLoggedIn will be called now
                onLoginListener.onLogin();
            } }, 
            new Response.ErrorListener() 
            {
                @Override
                public void onErrorResponse(VolleyError error) {
                    // TODO Auto-generated method stub
                    Log.d("VOLLEYERROR","error => "+error.toString());
                }
            }
                ) {
        };    
        // Just getting the Volley request queue from my application class (GetApplicatio.java), and adding request
        GetApplication.getRequestQueue().add(loginRequest);
    }
    

    【讨论】:

      【解决方案5】:

      我想在 Gabriel 的回答中添加一些内容。虽然RequestFuture 阻塞了调用它的线程并且它服务于您的目的,但网络请求本身并没有在该线程中执行。相反,它是在后台线程上执行的。

      根据我在浏览库后的理解,RequestQueue 中的请求在其start() 方法中调度:

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

      现在CacheDispatcherNetworkDispatcher 类都扩展了线程。因此,有效地产生了一个新的工作线程来使请求队列出队,并将响应返回到RequestFuture 内部实现的成功和错误侦听器。

      所以我认为创建一个单独的阻塞线程来使用RequestFuture 毫无意义。相反,正如 Makibo 在他的回答中提到的那样 - “使用回调监听器 onSuccess(在这种情况下为 SignUpResponseListener)并将代码放在那里。”

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2015-10-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多