Volley — to biblioteka sieciowa dla Androida, opracowana przez Google do wydajnego wykonywania żądań HTTP i ładowania obrazów. Biblioteka automatycznie zarządza pulą wątków, buforuje odpowiedzi i priorytetyzuje żądania. Według Google, 2025, Volley pozostaje popularnym wyborem dla projektów, które wymagają szybkiego startu bez konfigurowania złożonych zależności.
Najważniejsze
Volley — to biblioteka do komunikacji sieciowej w aplikacjach Android, zaprezentowana przez Google na konferencji I/O 2013. Nazwa Volley oznacza „salwę” — biblioteka jest przeznaczona do wykonywania wielu równoległych szybkich żądań, typowych dla aplikacji zorientowanych na UI, gdzie ważna jest szybkość reakcji interfejsu.
Volley została stworzona jako rozwiązanie problemów HttpURLConnection i AsyncTask: ręczne zarządzanie wątkami, brak buforowania, trudność priorytetyzacji żądań i rozwlekły kod. Google pozycjonowało Volley jako bibliotekę do operacji typu „fire-and-forget” — małych żądań, których wynik natychmiast wyświetla się w interfejsie.
Architektura Volley obejmuje trzy główne komponenty: RequestQueue (menedżer kolejki), CacheDispatcher (wątek dla buforowanych odpowiedzi) i NetworkDispatcher (wątki sieciowe). Taka architektura automatycznie rozdziela żądania: najpierw sprawdzany jest cache, a dopiero w przypadku jego braku wykonywane jest żądanie sieciowe. Zmniejsza to opóźnienie dla powtarzających się danych o 50–80%.
RequestQueue — centralna klasa Volley. Dodaje się do niej obiekty Request<T>, a kolejka automatycznie rozdziela je na dwa typy wątków: CacheDispatcher (jeden wątek, obsługuje żądania z możliwym cache) i NetworkDispatcher (wiele wątków, wykonują rzeczywiste żądania HTTP). Domyślnie Volley tworzy 4 wątki sieciowe.
Po dodaniu żądania RequestQueue sprawdza, czy można je obsłużyć z cache. Jeśli cache zawiera aktualną odpowiedź, CacheDispatcher zwraca ją natychmiast, bez żądania sieciowego. Jeśli cache jest nieaktualny lub go brak, żądanie jest przekazywane do NetworkDispatcher. Priorytet żądania (low, normal, high, immediate) określa kolejność przetwarzania w kolejce — żądania z priorytetem high są obsługiwane wcześniej niż normal.
Po wykonaniu żądania wynik jest dostarczany do głównego wątku (UI thread) przez Handler. Volley automatycznie przełącza callbacki onResponse() i onErrorResponse() na główny wątek, więc można bezpośrednio aktualizować interfejs w callbacku bez dodatkowych przełączeń. Upraszcza to kod i eliminuje całą klasę błędów związanych z wątkami.
Kolejną cechą Volley jest automatyczna deduplikacja żądań. Jeśli do kolejki dodano dwa identyczne żądania GET do tego samego URL z tymi samymi parametrami, Volley wykonuje tylko jedno z nich i zwraca tę samą odpowiedź do obu callbacków. Jest to szczególnie przydatne na ekranach, gdzie kilka komponentów niezależnie żąda tych samych danych — na przykład profil użytkownika, który jest jednocześnie potrzebny nagłówkowi i fragmentowi z ustawieniami.
Każde żądanie przechodzi przez sekwencję kroków: utworzenie Request, dodanie do RequestQueue, sprawdzenie cache (CacheDispatcher), wykonanie żądania HTTP (NetworkDispatcher), parsowanie odpowiedzi przez Response.Listener, dostarczenie wyniku do wątku UI. Przy anulowaniu żądania (cancel) RequestQueue usuwa je z kolejki i zapobiega wywołaniu callbacków.
Volley obsługuje również RetryPolicy, który określa liczbę ponownych prób przy awariach. DefaultRetryPolicy domyślnie wykonuje jedną ponowną próbę z timeoutem 2,5 sekundy. Dla niestabilnych połączeń liczbę prób można zwiększyć do 3, a timeout — do 10 sekund. Niestandardowy RetryPolicy implementuje się przez interfejs RetryPolicy z metodami getCurrentTimeout, getCurrentRetryCount i retry.
Volley udostępnia gotowe typy żądań dla popularnych formatów danych. Każdy typ implementuje abstrakcyjną klasę Request<T> i określa sposób parsowania odpowiedzi. Dla niestandardowych formatów można utworzyć własny typ, nadpisując metodę parseNetworkResponse.
| Typ żądania | Zwracany typ | Przeznaczenie |
|---|---|---|
| StringRequest | String | Pobieranie surowej odpowiedzi tekstowej |
| JsonObjectRequest | JSONObject | Parsowanie obiektu JSON |
| JsonArrayRequest | JSONArray | Parsowanie tablicy JSON |
| ImageRequest | Bitmap | Ładowanie i dekodowanie obrazu |
| ClearCacheRequest | — | Czyszczenie cache Volley |
Do pracy z Gson lub Kotlinx Serialization można utworzyć niestandardowy Request<T>, który w parseNetworkResponse używa wybranego parsera. Pozwala to uzyskiwać typowane obiekty bezpośrednio, pomijając ręczne parsowanie JSONObject. Takie podejście jest szczególnie przydatne w projektach już używających serializacji przez Gson lub Moshi.
Do wysyłania danych Volley obsługuje trzy typy ciała: JSONObject (przez JsonObjectRequest z metodą POST), Form-encoded (przez HashMap<String, String> w konstruktorze) i Multipart (przez niestandardowy MultipartRequest). Żądania Multipart są przydatne do przesyłania obrazów i plików, ale wymagają ręcznej implementacji, ponieważ Volley nie ma wbudowanej obsługi multipart/form-data w przeciwieństwie do OkHttp lub Dio.
Ograniczenia Volley stają się widoczne przy pracy z dużymi odpowiedziami. Volley ładuje całą odpowiedź do pamięci przed przekazaniem do callbacku, co może spowodować OutOfMemoryError dla plików JSON większych niż 10–20 MB. Do ładowania dużych plików Volley się nie nadaje — użyj DownloadManager lub OkHttp z strumieniowym ResponseBody. Volley nie obsługuje również wznawiania przerwanych pobrań (Range header) i nie działa z protokołami strumieniowymi takimi jak Server-Sent Events czy WebSocket w czasie rzeczywistym.
Rozważmy podstawowy przykład — StringRequest do pobierania danych z serwera. Najpierw tworzy się RequestQueue przez Volley.newRequestQueue(context). Następnie formułuje się żądanie z URL i callbackami na sukces i błąd.
val queue = Volley.newRequestQueue(context)
val request = StringRequest(
Request.Method.GET,
"https://api.github.com/users/octocat",
{ response ->
println("Odpowiedź: $response")
},
{ error ->
println("Błąd: ${error.message}")
}
)
queue.add(request)
Dla żądania JSON używa się JsonObjectRequest, który automatycznie parsuje odpowiedź do JSONObject. Volley obsługuje żądania GET i POST. Dla POST przekazuje się JSONObject w ciele żądania.
val jsonBody = JSONObject()
jsonBody.put("name", "New Repo")
jsonBody.put("description", "Created via Volley")
val request = JsonObjectRequest(
Request.Method.POST,
"https://api.github.com/user/repos",
jsonBody,
{ response ->
println("Utworzono: ${response.getString("id")}")
},
{ println("Błąd: $it") }
)
queue.add(request)
Do anulowania żądania używa się metody cancel() lub anulowania grupowego po tagu. Przy anulowaniu Volley nie wywołuje ani onResponse, ani onErrorResponse, co zapobiega aktualizacji interfejsu po opuszczeniu ekranu. Jest to ważne dla zapobiegania wyciekom pamięci w Activity i Fragment.
request.tag = "profile_request"
queue.add(request)
// Anulowanie przy opuszczaniu ekranu
queue.cancelAll("profile_request")
ImageLoader — to klasa opakowująca RequestQueue, zoptymalizowana do ładowania obrazów. Obsługuje cache pamięciowy (LruCache) i automatycznie anuluje żądania przy ponownym użyciu ImageView na listach RecyclerView. ImageLoader skaluje również obrazy do rozmiaru View, oszczędzając pamięć.
NetworkImageView — to niestandardowy View, który integruje się z ImageLoader i automatycznie zarządza ładowaniem: ustawia placeholder podczas ładowania, zastępuje błędem przy awarii i anuluje żądanie przy opuszczeniu ekranu przez View. DefaultImageUrlLoader ładuje obraz po URL i zapisuje go w LruCache dla szybkiego ponownego wyświetlenia.
Do użycia ImageLoader wystarczy utworzyć instancję przez ImageLoader(queue, ImageCache), gdzie ImageCache to implementacja interfejsu ImageCache z LruCache wewnątrz. NetworkImageView w XML łączy się z ImageLoader przez metodę setImageUrl(), a całe ładowanie odbywa się całkowicie automatycznie bez dodatkowego kodu do obsługi placeholder i błędów.
Tworzenie RequestQueue w każdym Activity — częsty błąd prowadzący do duplikowania wątków i zamieszania w cache. RequestQueue zaleca się tworzyć raz w Application lub przez klasę singleton. W przeciwnym razie każdy ekran będzie miał własną pulę wątków, a cache będzie przechowywany osobno dla każdej kolejki.
Ignorowanie anulowania żądań przy obrocie ekranu. Przy zmianie konfiguracji Activity jest odtwarzane, a callbacki starego Activity pozostają w pamięci. Prowadzi to do wycieku i próby aktualizacji zniszczonego View. Zawsze anuluj żądania w onStop() przez cancelAll() z tagiem specyficznym dla Activity.
Volley nie obsługuje HTTP/2 i korutyn — to nie jest błąd użycia, ale ograniczenie architektoniczne. Volley został stworzony w 2013 roku i nie obsługuje nowoczesnych protokołów ani korutyn Kotlin. Dla nowych projektów Google zaleca Retrofit + OkHttp. Volley nadaje się tylko do wsparcia projektów leganych lub prostych aplikacji z minimalnymi wymaganiami sieciowymi.
Często zadawane pytania
Volley jest przestarzały dla nowych projektów — Google nie aktualizował biblioteki od 2017 roku. Do nowoczesnych aplikacji używaj Retrofit + OkHttp lub Ktor Client. Volley może być stosowany tylko do wsparcia istniejącego kodu leganego lub w prostych projektach edukacyjnych z minimalnymi zadaniami sieciowymi.
Brak wsparcia nowoczesnych technologii: HTTP/2, korutyn Kotlin, wieloplatformowości i typowanej serializacji. Volley używa JSONObject i JSONArray bez typów, co prowadzi do błędów runtime przy niezgodności struktury JSON z oczekiwaniami.
Przez ImageLoader i NetworkImageView. ImageLoader używa LruCache do buforowania obrazów w pamięci i automatycznie anuluje żądania przy ponownym użyciu View. NetworkImageView wyświetla placeholder podczas ładowania i zastępuje go gotowym obrazem lub wskaźnikiem błędu.
Technicznie tak — przez opakowanie suspendCoroutine { } wokół callbacków Volley. Nie daje to jednak korzyści, ponieważ Volley nie obsługuje anulowania przez anulowanie korutyny i nie współpracuje bezpośrednio z Dispatchers.IO. Lepiej użyć Ktor Client z natywnym wsparciem korutyn.
Timeout konfiguruje się przez RetryPolicy. Domyślnie DefaultRetryPolicy używa timeout 2,5 sekundy i jedną ponowną próbę. Zmiana parametrów: request.retryPolicy = DefaultRetryPolicy(10000, 1, 1.0f) — 10 sekund timeoutu, jedna próba.
Podsumowanie
Opracujemy aplikację mobilną pod klucz
IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.
Przeczytaj również