تنزيل ملف على Android: من الفكرة إلى الكود العملي
\n\nكل مطور Android يواجه عاجلاً أم آجلاً مهمة تحميل ملف من الشبكة. قد يكون ذلك تحديثًا للتطبيق، أو تنزيل صورة، أو مستند PDF، أو مقطع موسيقي. لكن مجرد تنزيل الملف هو نصف المهمة. يجب أن يرى المستخدم أن العملية جارية: كم تم التنزيل، كم تبقى، وأن التطبيق لم يتجمد.
\n\nفي هذه المقالة، سنحلل سؤالًا كلاسيكيًا من Stack Overflow حصل على آلاف المشاهدات: كيفية تنزيل ملف على Android وعرض التقدم في نافذة حوارية. سنكتب الكود بلغة Java باستخدام AsyncTask و ProgressDialog (على الرغم من أن المشاريع الحديثة تستبدلها غالبًا بـ coroutines أو RxJava، إلا أن فهم الأساسيات هو قاعدة لا تزول).
هل أنت مستعد؟ لنبدأ!
\n\nلماذا هو صعب جدًا؟ ثلاث مطبات في تحميل Android
\n\nللوهلة الأولى، المهمة بسيطة: فتح اتصال، قراءة تدفق البايتات، الكتابة إلى ملف. لكن عمليًا، يتعثر المطورون في ثلاثة أشياء:
\n\n- \n
- حظر سلسلة واجهة المستخدم — إذا بدأت التحميل في السلسلة الرئيسية، سيتجمد التطبيق وسيظهر Android رسالة "التطبيق لا يستجيب". \n
- عدم تحديث التقدم — حتى لو قمت بحساب البايتات، لا يمكنك تغيير واجهة المستخدم من سلسلة خلفية. \n
- تغيير التهيئة — يدير المستخدم الشاشة، ويتم إعادة إنشاء
Activity. يختفي الحوار، ويستمر التحميل في الفراغ. \n
سنحل المشكلات الثلاث. سنبدأ بالهندسة المعمارية، ثم نكتب الكود.
\n\nاختيار الأدوات: AsyncTask و ProgressDialog
\n\nلأغراض تعليمية، نستخدم النهج الكلاسيكي:
\n- \n
AsyncTask— لتنفيذ العمل الخلفي ونشر التقدم. \n ProgressDialog— لعرض النسب المئوية. \n HttpURLConnection— لطلب HTTP (لا يزال حيًا، رغم أنه قديم). \n
هام: في المشاريع الحقيقية اليوم، غالبًا ما يستخدمون DownloadManager أو WorkManager أو مكتبات مثل Retrofit + OkHttp. لكن فهم كيفية عمل التحميل "يدويًا" هو أمر لا بد منه لأي مطور.
الكود الكامل: تنزيل ملف مع ProgressDialog
\n\nأدناه فئة جاهزة للتحميل. الكود معلق بالعربية لتتمكن من فهم ما يحدث فورًا.
\n\nimport android.app.ProgressDialog;\nimport android.content.Context;\nimport android.os.AsyncTask;\nimport android.os.Environment;\nimport android.widget.Toast;\n\nimport java.io.File;\nimport java.io.FileOutputStream;\nimport java.io.InputStream;\nimport java.net.HttpURLConnection;\nimport java.net.URL;\n\npublic class DownloadFileTask extends AsyncTask<String, Integer, String> {\n\n private Context context;\n private ProgressDialog progressDialog;\n\n public DownloadFileTask(Context context) {\n this.context = context;\n }\n\n @Override\n protected void onPreExecute() {\n super.onPreExecute();\n // إنشاء وعرض حوار التقدم\n progressDialog = new ProgressDialog(context);\n progressDialog.setTitle("تحميل الملف");\n progressDialog.setMessage("جاري التنزيل...");\n progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);\n progressDialog.setCancelable(false); // منع الإغلاق العرضي\n progressDialog.show();\n }\n\n @Override\n protected String doInBackground(String... params) {\n String fileUrl = params[0];\n String fileName = "downloaded_file.pdf"; // يمكن تمريره كمعامل ثانٍ\n InputStream inputStream = null;\n FileOutputStream outputStream = null;\n\n try {\n // 1. إنشاء الاتصال\n URL url = new URL(fileUrl);\n HttpURLConnection connec