Volley - http-запрос блокирующим способом
Я изучаю, как использовать Google Volley в наши дни. Это очень удобно для быстрой работы в сети. Кажется, что все запросы выполняются в фоновом режиме в Volley. Например:
volleyRequestQueue.add(new JsonObjectRequest(Method.POST, SIGNUP_URL, reqBody, new SignUpResponseListener(), new MyErrorListener()));
Используя приведенный выше код, мы можем сделать POST-вызов, который работает в фоновом режиме (неблокирующий способ). Теперь мой вопрос:
Можно ли заблокировать POST-вызов? Почему мне нужен блокирующий способ сделать звонок REST? Потому что некоторые вызовы, такие как вход, должны быть выполнены, прежде чем делать что-то еще.
Спасибо
Ответы
Ответ 1
Volley поддерживает запрос блокировки через RequestFutures. Вы создаете обычный запрос, но устанавливаете его обратные вызовы как ваш запрос в будущем, что является просто расширением волей стандартного фьючерса 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) {
}
Ответ 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 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, используйте в этом случае прослушиватель callback onSuccess (SignUpResponseListener) и поместите туда код. Это лучшая практика.
Ответ 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
Я хочу что-то добавить к ответу Габриэля. Пока RequestFuture
блокирует поток, из которого он вызывается, и он служит вашей цели, сам сетевой запрос не выполняется в этом потоке. Вместо этого он выполняется на фоновом потоке.
Из того, что я понимаю после прохождения библиотеки, запросы в RequestQueue
отправляются в методе start()
:
public void start() {
....
mCacheDispatcher = new CacheDispatcher(...);
mCacheDispatcher.start();
....
NetworkDispatcher networkDispatcher = new NetworkDispatcher(...);
networkDispatcher.start();
....
}
Теперь оба класса CacheDispatcher
и NetworkDispatcher
расширяют поток. Таким образом, создается новый рабочий поток для отбрасывания очереди запросов, и ответ возвращается получателям успеха и ошибок, реализованным внутри RequestFuture
.
Поэтому я не вижу смысла использовать отдельный блокирующий поток для использования RequestFuture
. Вместо этого, как сказал Макибо в своем ответе, "используйте прослушиватель обратного вызова onSuccess (SignUpResponseListener в этом случае) и поместите там код".