Volley هي مكتبة شبكات لتطبيقات Android طورتها Google لتنفيذ طلبات HTTP بكفاءة وتحميل الصور. تدير المكتبة تلقائياً مجموعة من الخيوط، وتخزن الاستجابات في الذاكرة المؤقتة، وتحدد أولويات الطلبات. وفقاً Google, 2025، تظل Volley خياراً شائعاً للمشاريع التي تتطلب بداية سريعة دون تهيئة تبعيات معقدة.
الوجبات الرئيسية
Volley هي مكتبة للتواصل الشبكي في تطبيقات Android، قدمتها Google في مؤتمر I/O 2013. اسم Volley يعني «طلقة مدفعية» — المكتبة مصممة لتنفيذ طلبات سريعة متعددة بالتوازي، وهي سمة التطبيقات الموجهة لواجهة المستخدم حيث سرعة الاستجابة مهمة.
تم إنشاء Volley كحل لمشاكل HttpURLConnection وAsyncTask: الإدارة اليدوية للخيوط، عدم وجود تخزين مؤقت، تعقيد تحديد أولويات الطلبات، والكود المتضخم. وضعت Google Volley كمكتبة لعمليات «fire-and-forget» — طلبات صغيرة تظهر نتائجها فوراً في واجهة المستخدم.
تتضمن بنية Volley ثلاثة مكونات رئيسية: RequestQueue (مدير قائمة الانتظار)، CacheDispatcher (خيط للاستجابات المخزنة مؤقتاً)، وNetworkDispatcher (خيوط الشبكة). توزع هذه البنية الطلبات تلقائياً: يتم التحقق من الذاكرة المؤقتة أولاً، وفقط في حالة عدم وجودها يتم تنفيذ طلب الشبكة. هذا يقلل زمن الوصول للبيانات المتكررة بنسبة 50–80%.
RequestQueue هي الفئة المركزية في Volley. تتم إضافة كائنات Request<T> إليها، وتقوم قائمة الانتظار بتوزيعها تلقائياً على نوعين من الخيوط: CacheDispatcher (خيط واحد، يعالج الطلبات ذات التخزين المؤقت المحتمل) وNetworkDispatcher (عدة خيوط، تنفذ طلبات HTTP الفعلية). بشكل افتراضي، تنشئ Volley 4 خيوط شبكة.
عند إضافة طلب، تتحقق RequestQueue مما إذا كان يمكن خدمته من الذاكرة المؤقتة. إذا كانت الذاكرة المؤقتة تحتوي على استجابة محدثة، يقوم CacheDispatcher بإرجاعها فوراً دون طلب شبكة. إذا كانت الذاكرة المؤقتة قديمة أو غير موجودة، يتم تمرير الطلب إلى NetworkDispatcher. أولوية الطلب (low, normal, high, immediate) تحدد ترتيب المعالجة داخل قائمة الانتظار — تتم معالجة الطلبات ذات الأولوية العالية قبل الطلبات العادية.
بعد تنفيذ الطلب، يتم تسليم النتيجة إلى الخيط الرئيسي (UI thread) عبر Handler. تقوم Volley تلقائياً بتحويل callback onResponse() وonErrorResponse() إلى الخيط الرئيسي، لذلك يمكن تحديث الواجهة مباشرة في callback دون تبديل خيوط إضافي. هذا يبسط الكود ويزيل فئة كاملة من أخطاء الخيوط.
ميزة أخرى لـ Volley هي إلغاء تكرار الطلبات تلقائياً. إذا تمت إضافة طلبين GET متطابقين لنفس URL بنفس المعلمات إلى قائمة الانتظار، تنفذ Volley واحداً فقط منهما وتعيد نفس الاستجابة لكلا callback. هذا مفيد بشكل خاص للشاشات حيث تطلب مكونات متعددة بشكل مستقل نفس البيانات — على سبيل المثال، ملف تعريف المستخدم الذي يحتاجه كل من الرأس وجزء الإعدادات في نفس الوقت.
كل طلب يمر عبر سلسلة من الخطوات: إنشاء Request، إضافته إلى RequestQueue، التحقق من الذاكرة المؤقتة (CacheDispatcher)، تنفيذ طلب HTTP (NetworkDispatcher)، تحليل الاستجابة عبر Response.Listener، تسليم النتيجة إلى خيط واجهة المستخدم. عند إلغاء طلب (cancel)، تقوم RequestQueue بإزالته من قائمة الانتظار وتمنع استدعاء callbacks.
تدعم Volley أيضاً RetryPolicy، الذي يحدد عدد محاولات إعادة المحاولة عند الفشل. يقوم DefaultRetryPolicy افتراضياً بإجراء محاولة إعادة واحدة بفترة مهلة 2.5 ثانية. للاتصالات غير المستقرة، يمكن زيادة عدد المحاولات إلى 3 والمهلة إلى 10 ثوانٍ. يتم تنفيذ RetryPolicy مخصصة عبر واجهة RetryPolicy مع طرق getCurrentTimeout وgetCurrentRetryCount وretry.
توفر Volley أنواع طلبات جاهزة لتنسيقات البيانات الشائعة. كل نوع يطبق الفئة المجردة Request<T> ويحدد طريقة لتحليل الاستجابة. للتنسيقات المخصصة، يمكنك إنشاء نوع خاص بك عن طريق تجاوز طريقة parseNetworkResponse.
| نوع الطلب | نوع الإرجاع | الغرض |
|---|---|---|
| StringRequest | String | الحصول على استجابة نصية خام |
| JsonObjectRequest | JSONObject | تحليل كائن JSON |
| JsonArrayRequest | JSONArray | تحليل مصفوفة JSON |
| ImageRequest | Bitmap | تحميل وفك تشفير صورة |
| ClearCacheRequest | — | مسح ذاكرة التخزين المؤقت لـ Volley |
للعمل مع Gson أو Kotlinx Serialization، يمكنك إنشاء Request<T> مخصص يستخدم المحلل المختار في parseNetworkResponse. هذا يسمح باستقبال كائنات مكتوبة مباشرة، متجاوزاً التحليل اليدوي لـ JSONObject. هذا النهج مفيد بشكل خاص للمشاريع التي تستخدم بالفعل التسلسل عبر Gson أو Moshi.
لإرسال البيانات، تدعم Volley ثلاثة أنواع من الجسم: JSONObject (عبر JsonObjectRequest بطريقة POST)، Form-encoded (عبر HashMap<String, String> في المنشئ)، وMultipart (عبر MultipartRequest مخصص). طلبات Multipart مفيدة لتحميل الصور والملفات ولكنها تتطلب تنفيذاً يدوياً لأن Volley لا تحتوي على دعم مدمج لـ multipart/form-data، على عكس OkHttp أو Dio.
تصبح قيود Volley ملحوظة عند العمل مع الاستجابات الكبيرة. تقوم Volley بتحميل الاستجابة بأكملها في الذاكرة قبل تمريرها إلى callback، مما قد يسبب OutOfMemoryError لملفات JSON الأكبر من 10–20 ميغابايت. لتنزيل الملفات الكبيرة، Volley غير مناسبة — استخدم DownloadManager أو OkHttp مع ResponseBody المتدفق. كما أن Volley لا تدعم استئناف التنزيلات المتقطعة (رأس Range) ولا تعمل مع بروتوكولات التدفق مثل Server-Sent Events أو WebSocket في الوقت الفعلي.
لنلقِ نظرة على مثال أساسي — StringRequest لجلب البيانات من الخادم. أولاً، يتم إنشاء RequestQueue عبر Volley.newRequestQueue(context). ثم يتم تشكيل طلب مع URL و callbacks للنجاح والخطأ.
val queue = Volley.newRequestQueue(context)
val request = StringRequest(
Request.Method.GET,
"https://api.github.com/users/octocat",
{ response ->
println("الاستجابة: $response")
},
{ error ->
println("خطأ: ${error.message}")
}
)
queue.add(request)
لـ طلب JSON، يتم استخدام JsonObjectRequest، الذي يحلل الاستجابة تلقائياً إلى JSONObject. تدعم Volley طلبات GET وPOST. لـ POST، يتم تمرير JSONObject في نص الطلب.
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("تم الإنشاء: ${response.getString("id")}")
},
{ println("خطأ: $it") }
)
queue.add(request)
لإلغاء طلب، يتم استخدام طريقة cancel() أو الإلغاء الجماعي حسب العلامة. عند الإلغاء، لا تستدعي Volley لا onResponse ولا onErrorResponse، مما يمنع تحديثات الواجهة بعد مغادرة الشاشة. هذا مهم لمنع تسرب الذاكرة في Activity وFragment.
request.tag = "profile_request"
queue.add(request)
// الإلغاء عند مغادرة الشاشة
queue.cancelAll("profile_request")
ImageLoader هو فئة مغلفة حول RequestQueue، محسنة لتحميل الصور. يدعم ذاكرة التخزين المؤقت (LruCache) ويلغي تلقائياً الطلبات عند إعادة استخدام ImageView في قوائم RecyclerView. كما يقوم ImageLoader بقياس الصور لتناسب حجم View، مما يوفر الذاكرة.
NetworkImageView هو View مخصص يتكامل مع ImageLoader ويدير التحميل تلقائياً: يعرض عنصراً نائباً أثناء التحميل، ويستبدله بخطأ عند الفشل، ويلغي الطلب عند مغادرة View للشاشة. يقوم DefaultImageUrlLoader بتحميل صورة عبر URL وتخزينها في LruCache لإعادة عرض سريعة.
لاستخدام ImageLoader، ما عليك سوى إنشاء مثيل عبر ImageLoader(queue, ImageCache)، حيث ImageCache هو تنفيذ لواجهة ImageCache مع LruCache داخلياً. يتم ربط NetworkImageView في تخطيط XML بـ ImageLoader عبر طريقة setImageUrl()، ويحدث كل التحميل تلقائياً بالكامل دون كود إضافي للتعامل مع العناصر النائبة والأخطاء.
إنشاء RequestQueue في كل Activity هو خطأ شائع يؤدي إلى ازدواجية الخيوط وارتباك في الذاكرة المؤقتة. يُنصح بإنشاء RequestQueue مرة واحدة في Application أو عبر فئة singleton. خلاف ذلك، سيكون لكل شاشة مجموعة الخيوط الخاصة بها، وسيتم تخزين الذاكرة المؤقتة بشكل منفصل لكل قائمة انتظار.
تجاهل إلغاء الطلبات عند تدوير الشاشة. عند تغيير التكوين، يتم إعادة إنشاء Activity، وتستمر callbacks النشاط القديم في الذاكرة. هذا يؤدي إلى تسرب ومحاولات تحديث View مدمر. قم دائماً بإلغاء الطلبات في onStop() عبر cancelAll() بعلامة خاصة بـ Activity.
Volley لا تدعم HTTP/2 و coroutines — هذا ليس خطأ استخدام بل قيد معماري. تم إنشاء Volley في عام 2013 ولا تدعم البروتوكولات الحديثة و coroutines الخاصة بـ Kotlin. للمشاريع الجديدة، توصي Google باستخدام Retrofit + OkHttp. Volley مناسبة فقط لدعم المشاريع القديمة أو التطبيقات البسيطة ذات متطلبات الشبكة البسيطة.
الأسئلة الشائعة
Volley قديمة للمشاريع الجديدة — لم تقم Google بتحديث المكتبة منذ 2017. للتطبيقات الحديثة، استخدم Retrofit + OkHttp أو Ktor Client. يمكن استخدام Volley فقط لدعم الكود القديم الحالي أو في المشاريع التعليمية البسيطة ذات مهام الشبكة البسيطة.
نقص الدعم للتقنيات الحديثة: HTTP/2، coroutines الخاصة بـ Kotlin، التطوير عبر المنصات، والتسلسل المكتوب. تستخدم Volley JSONObject وJSONArray بدون أنواع، مما يؤدي إلى أخطاء وقت التشغيل عندما لا يتطابق هيكل JSON مع التوقعات.
عبر ImageLoader وNetworkImageView. يستخدم ImageLoader LruCache للتخزين المؤقت للصور في الذاكرة ويلغي تلقائياً الطلبات عند إعادة استخدام العناصر. يعرض NetworkImageView عنصراً نائباً أثناء التحميل ويستبدله بالصورة المحملة أو مؤشر الخطأ.
نعم من الناحية الفنية — عبر غلاف suspendCoroutine { } حول callbacks Volley. لكن هذا لا يوفر مزايا لأن Volley لا تدعم الإلغاء بناءً على إلغاء coroutine ولا تعمل مع Dispatchers.IO مباشرة. من الأفضل استخدام Ktor Client مع دعم coroutine الأصلي.
يتم تهيئة المهلة عبر RetryPolicy. افتراضياً، يستخدم DefaultRetryPolicy مهلة 2.5 ثانية ومحاولة إعادة واحدة. لتغيير المعلمات: request.retryPolicy = DefaultRetryPolicy(10000, 1, 1.0f) — 10 ثوانٍ مهلة، محاولة واحدة.
الملخص
سنقوم بتطوير تطبيق جوال جاهز
تقدم IT Sectr تطبيقات iOS وAndroid للشركات الناشئة والشركات منذ عام 2017. سوف نقدم لك النصح ونقترح أفضل حل.
اقرأ أيضًا