Czy mogę wykonać synchroniczną prośbę z volley?
Wyobraź sobie, że jestem w Serwisie, który ma już wątek w tle. Czy Mogę zrobić prośbę używając tego samego wątku, żeby wywołania były synchroniczne?
Istnieją 2 powody tego: - Po pierwsze, nie potrzebuję innego wątku i byłoby marnotrawstwem, aby go utworzyć. - Po drugie, jeśli jestem w serwisie, wykonanie wątku zakończy się przed wywołaniem zwrotnym, a do tego nie będę miał odpowiedzi od Volley. Wiem, że mogę stworzyć własny serwis, który ma jakiś wątek z runloop mogę kontrolować, ale pożądane byłoby posiadanie tej funkcjonalności w volley.
Dziękuję!
6 answers
Wygląda na to, że jest to możliwe z klasą RequestFuture
. Na przykład, aby utworzyć synchroniczne żądanie JSON HTTP GET, możesz wykonać następujące czynności:
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
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-02-10 17:30:54
Uwaga @ Matthews odpowiedź jest poprawna, ale jeśli jesteś w innym wątku i wykonujesz połączenie volley, gdy nie masz internetu, twój błąd callback zostanie wywołany w głównym wątku, ale wątek, w którym jesteś, zostanie zablokowany na zawsze. (dlatego jeśli ten wątek jest Intentserwisem, nigdy nie będziesz w stanie wysłać do niego kolejnej wiadomości, a Twoja usługa będzie w zasadzie Martwa).
Użyj wersji get()
, która ma limit czasu future.get(30, TimeUnit.SECONDS)
i złap błąd, aby zamknąć wątek.
To match @Mathews ODPOWIEDŹ:
try {
return future.get(30, TimeUnit.SECONDS);
} catch (InterruptedException e) {
// exception handling
} catch (ExecutionException e) {
// exception handling
} catch (TimeoutException e) {
// exception handling
}
Poniżej owinąłem go metodą i użyłem innego żądania:
/**
* 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;
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-05-22 13:52:26
Prawdopodobnie zaleca się użycie Futures, ale jeśli z jakiegoś powodu nie chcesz, zamiast gotować własną synchronizowaną blokadę powinieneś użyć java.util.concurrent.CountDownLatch
. Więc to by tak działało..
//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...
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-06-30 08:00:04
Jako komplementarne spostrzeżenie zarówno @Blundells, jak i @Mathews odpowiedzi, nie jestem pewien jakiekolwiekwywołanie jest dostarczane do czegokolwiek , Ale głównego wątku przez Volley.
Źródło
Przeglądając RequestQueue
implementacja wygląda na to, że RequestQueue
używa NetworkDispatcher
do wykonania żądania i ResponseDelivery
do dostarczenia wyniku (ResponseDelivery
jest wstrzykiwana do NetworkDispatcher
). {[3] } jest z kolei tworzony z Handler
spawn z głównego wątku (gdzieś wokół linii 112 w implementacji RequestQueue
).
Gdzieś o linii 135 w NetworkDispatcher
implementacja wydaje się, że również udane wyniki są dostarczane przez to samo ResponseDelivery
Jak wszelkie błędy. Ponownie; a ResponseDelivery
oparte na spawnie Handler
z głównego wątku.
Uzasadnienie
W przypadku użycia, gdy wniosek ma być złożony z IntentService
, można założyć, że wątek Serwisu powinien zostać zablokowany do czasu uzyskania odpowiedzi od Volley (aby zagwarantować żywy zakres runtime do obsługi wyniku w).
Sugerowane rozwiązania
Jednym z podejść byłoby nadpisanie domyślnej drogi a RequestQueue
jest tworzony , gdzie zamiast tego używany jest alternatywny konstruktor, wstrzykując ResponseDelivery
, który pojawia się z bieżącego wątku, a nie głównego wątku. Nie zbadałem jednak konsekwencji tego.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-04-20 09:28:00
Używam blokady, aby osiągnąć ten efekt teraz zastanawiam się, czy to poprawny mój sposób ktoś chce coś skomentować ?
// 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();
}
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-11-06 17:21:20
Chcę dodać coś do zaakceptowanej odpowiedzi Mateusza. Chociaż RequestFuture
może wydawać się, że wykonuje synchroniczne wywołanie z utworzonego wątku, to tak nie jest. Zamiast tego wywołanie jest wykonywane w wątku tła.
Z tego co rozumiem po przejrzeniu biblioteki, żądania w {[2] } są wysyłane w jej metodzie start()
:
public void start() {
....
mCacheDispatcher = new CacheDispatcher(...);
mCacheDispatcher.start();
....
NetworkDispatcher networkDispatcher = new NetworkDispatcher(...);
networkDispatcher.start();
....
}
Teraz obie klasy CacheDispatcher
i NetworkDispatcher
rozszerzają wątek. W ten sposób powstaje nowy wątek roboczy do usuwania kolejki żądań, a odpowiedź jest powrócił do sukcesu i błędów zaimplementowanych wewnętrznie przez RequestFuture
.
Chociaż twój drugi cel jest osiągnięty, ale twój pierwszy cel nie jest, ponieważ nowy wątek jest zawsze tworzony, bez względu na to, z którego wątku wykonujesz RequestFuture
.
W skrócie, prawdziwe żądanie synchroniczne nie jest możliwe przy domyślnej bibliotece Volley.Popraw mnie, jeśli się mylę.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-12-17 06:42:08