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 в этом случае) и поместите там код".