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ę!

Author: Ankit, 2013-06-03

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
}
 170
Author: Matthew,
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;
    }
 108
Author: Blundell,
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...
}
 7
Author: Timo,
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.

 2
Author: dbm,
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();
            }
        }
 1
Author: forcewill,
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ę.

 1
Author: Vignatus,
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