Могу ли я сделать синхронный запрос с volley?



представьте, что я нахожусь в службе, которая уже имеет фоновый поток. Могу ли я сделать запрос с помощью volley в том же потоке, так что обратные вызовы происходят синхронно?



есть 2 причины для этого:
- Во-первых, мне не нужна еще одна нить, и было бы бесполезно ее создавать.
- Во-вторых, если я нахожусь в ServiceIntent, выполнение потока завершится до обратного вызова, и поэтому у меня не будет ответа от Volley. Я знаю, что могу создать свой собственный сервис, который имеет некоторый поток с runloop я могу управлять, но хотелось бы иметь эту функциональность в volley.



спасибо!

431   6  

6 ответов:

похоже, что это возможно с залпом RequestFuture класса. Например, чтобы создать синхронный запрос HTTP GET JSON, можно выполнить следующие действия:

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
}

Примечание @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
        }

ниже я завернул его в метод и использовать другой запрос:

   /**
     * 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;
    }

вероятно, рекомендуется использовать фьючерсы, но если по какой-либо причине вы не хотите, вместо того, чтобы готовить свою собственную синхронизированную блокирующую вещь, вы должны использовать 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...
}

как дополнительное наблюдение к ответам @Blundells и @Mathews, я не уверен любой вызов доставляется на что угодно но основной поток залпом.

Источник

взглянув на RequestQueue реализация кажется RequestQueue использует NetworkDispatcher выполнить запрос и A ResponseDelivery чтобы доставить результат (the ResponseDelivery вводят в NetworkDispatcher). Элемент ResponseDelivery в свою очередь создано с помощью Handler икру из основного потока (где-то около линии 112 в RequestQueue реализации).

где-то около строки 135 в NetworkDispatcher реализация похоже, что и успешные результаты доставляются через то же самое ResponseDelivery как и любой ошибки. Снова;ResponseDelivery на основе Handler икра из основного потока.

обоснование

для случая использования, когда запрос должен быть сделан из IntentService это справедливо предположить, что поток службы должен блокироваться до тех пор, пока мы не получим ответ от Volley (чтобы гарантировать живую область выполнения для обработки результата).

предлагаемые решения

один из подходов заключается, чтобы переопределить значение по умолчанию, как RequestQueue создана, где вместо этого используется альтернативный конструктор, вводящий ResponseDelivery который порождает из настоящее поток, а не основной поток. Я не однако исследовал последствия этого.

Я использую блокировку для достижения этого эффекта теперь мне интересно, если его правильный мой путь кто хочет высказаться ?

// as a field of the class where i wan't to do the synchronous `volley` call   
Object mLock = new Object();


// need to have the error and success listeners notifyin
final boolean[] finished = {false};
            Response.Listener<ArrayList<Integer>> responseListener = new Response.Listener<ArrayList<Integer>>() {
                @Override
                public void onResponse(ArrayList<Integer> response) {
                    synchronized (mLock) {
                        System.out.println();
                        finished[0] = true;
                        mLock.notify();

                    }


                }
            };

            Response.ErrorListener errorListener = new Response.ErrorListener() {
                @Override
                public void onErrorResponse(VolleyError error) {
                    synchronized (mLock) {
                        System.out.println();
                        finished[0] = true;
                        System.out.println();
                        mLock.notify();
                    }
                }
            };

// after adding the Request to the volley queue
synchronized (mLock) {
            try {
                while(!finished[0]) {
                    mLock.wait();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

Я хочу добавить что-то к принятому ответу Мэтью. В то время как RequestFuture может показаться, что синхронный вызов из потока, который вы создали, это не так. Вместо этого вызов выполняется в фоновом потоке.

из того, что я понимаю после прохождения библиотеки, запросы в RequestQueue отправляется в start() способ:

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

теперь CacheDispatcher и NetworkDispatcher классы расширяют поток. Таким образом, эффективно создается новый рабочий поток для удаления очереди очередь запросов и ответ возвращаются слушателям success и error, реализованным внутри RequestFuture.

хотя ваша вторая цель достигнута, но вы не первая цель, так как новый поток всегда порождается, независимо от того, из какого потока вы выполняете RequestFuture.

короче, истинный синхронный запрос невозможен с библиотекой волейбола по умолчанию. поправьте меня, если я ошибаюсь.

Comments

    Ничего не найдено.