Intereting Posts
Android Studio не показывает javadoc с CTRL + Q, но доступен в источнике Как изменить цвет ProgressDialog Spinner в Android? IllegalArgumentException при выборе текста в Android TextView Не удается извлечь файл из ZIP-архива, созданного на Android (для конкретного устройства / ОС) Как я могу отсортировать список getScanResults () на основе силы сигнала в порядке возрастания? Есть ли другой платежный модуль, кроме Paypal? Robotium – Как установить дату в подборщике даты с помощью Robotium Каково использование файла res / values ​​/ public.xml на Android? LocationManager.getLastKnownLocation (LocationManager.NETWORK_PROVIDER) всегда возвращает NULL на Galaxy S7 (ТОЛЬКО) Не удается создать проект Android с Gradle (с помощью аннотаций Android) Исключение – не удалось разрешить атрибут по индексу 6: TypedValue Длительное обслуживание потребляет много батареи NullPointerException при отображении OptionsMenu Невозможно сделать пользовательский диалог DialogFragment прозрачным над фрагментом Отключить определенные даты дня в Android date picker

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

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

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

Спасибо!

Похоже, это возможно с классом Volley's RequestFuture . Например, чтобы создать синхронный запрос GET 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 } 

Примечание. Ответ @Matthews правильный, но если вы находитесь в другом потоке, и вы вызываете вызов волейбола, когда у вас нет интернета, ваш обратный вызов будет вызываться в основном потоке, но поток, на котором вы находитесь, будет заблокирован FOREVER. (Поэтому, если этот поток является IntentService, вы никогда не сможете отправить ему другое сообщение, и ваш сервис будет в основном мертвым).

Используйте версию get() которая имеет timeout 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 я не уверен, что любой вызов доставляется во что-либо, кроме основного потока Volley.

Источник

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

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

обоснование

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

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

Один из подходов состоит в том, чтобы переопределить способ по RequestQueue , создаваемый 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 расширяют поток. Таким образом, создается новый рабочий поток для отбрасывания очереди запросов, и ответ возвращается RequestFuture успеха и ошибок, реализованным внутри RequestFuture .

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

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