صباح يبدأ بـ NetworkOnMainThreadException
\n\nأنت تكتب تطبيقًا على Android. كل شيء يسير على ما يرام حتى تقرر إجراء طلب HTTP بسيط في زر. تضغط على "تحديث" - ويتعطل التطبيق مع تتبع مكدس أحمر. هل هذا مألوف؟ android.os.NetworkOnMainThreadException هو خطأ كلاسيكي شاهده كل مطور Android مرة واحدة على الأقل في حياته. في عام 2025، لا يزال هذا الخطأ قائمًا، ويستمر المبتدئون في الوقوع في نفس الفخ.
\n\nفي هذه المقالة، سنشرح لماذا يحدث NetworkOnMainThreadException، وكيفية إصلاحه في Java، وما هي الأساليب التي تعتبر أفضل الممارسات في عام 2025. ستحصل على حلول جاهزة مع كود يمكنك إدراجه فورًا في مشروعك.
\n\nلماذا يحدث NetworkOnMainThreadException؟
\n\nAndroid ليس مجرد Java. إنه نظام بقواعد صارمة. إحداها: لا تقم بتنفيذ عمليات الشبكة في الخيط الرئيسي (واجهة المستخدم). الخيط الرئيسي مسؤول عن رسم الواجهة ومعالجة اللمسات. إذا تم حظره ولو لثانية واحدة، سيرى المستخدم "تباطؤًا"، وإذا استمر لمدة خمس ثوانٍ، سيعرض النظام مربع حوار "التطبيق لا يستجيب" (ANR).
\n\nبدءًا من Android 3.0 (Honeycomb)، أضافت Google حماية: أي استدعاء شبكة في الخيط الرئيسي يرمي android.os.NetworkOnMainThreadException. يقول الاستثناء: "مرحبًا، أنت تفعل شيئًا خطيرًا لتجربة المستخدم!".
في عام 2025، لم تختفِ هذه القاعدة. حتى إذا كنت تستخدم مكتبات حديثة (Retrofit، OkHttp)، فلا يزال يتعين عليك تشغيلها في خيط خلفي. الاستثناء ليس خطأ، بل ميزة أمان.
\n\nأفضل 5 طرق لإصلاح الخطأ (Java، 2025)
\n\nسننظر في خمسة أساليب - من القديمة إلى الحديثة. اختر ما يناسب مشروعك.
\n\n1. AsyncTask الكلاسيكي (قديم ولكنه يعمل)
\n\nكان AsyncTask معيارًا قبل 10 سنوات. في عام 2025، لا يُنصح باستخدامه (تم إهماله في API 30+)، ولكن في المشاريع القديمة قد لا تزال تجده.
\n\nprivate class MyNetworkTask extends AsyncTask<String, Void, String> {\n @Override\n protected String doInBackground(String... params) {\n // هذا هو الخيط الخلفي\n try {\n URL url = new URL(params[0]);\n HttpURLConnection connection = (HttpURLConnection) url.openConnection();\n BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));\n StringBuilder result = new StringBuilder();\n String line;\n while ((line = reader.readLine()) != null) {\n result.append(line);\n }\n return result.toString();\n } catch (Exception e) {\n return "Error: " + e.getMessage();\n }\n }\n\n @Override\n protected void onPostExecute(String result) {\n // هذا هو خيط واجهة المستخدم\n textView.setText(result);\n }\n}\n\n// التشغيل:\nnew MyNetworkTask().execute("https://api.example.com/data");\n\nالعيوب: AsyncTask يصعب اختباره، وهو غير مناسب لسلاسل الطلبات، وفي حالة تدوير الشاشة قد يتسبب في تسرب الذاكرة. استخدمه فقط إذا كان المشروع مكتوبًا بلغة Java 7-8.
\n\n2. Java Thread + Handler (نهج نقي)
\n\nأبسط طريقة هي إنشاء خيط منفصل وإرجاع النتيجة إلى واجهة المستخدم عبر Handler.
\n\nprivate Handler mainHandler = new Handler(Looper.getMainLooper());\n\nprivate void fetchDataInBackground() {\n new Thread(new Runnable() {\n @Override\n public void run() {\n // الخيط الخلفي\n final String result = performNetworkCall();\n \n // العودة إلى خيط واجهة المستخدم\n mainHandler.post(new Runnable() {\n @Override\n public void run() {\n textView.setText(result);\n }\n });\n }\n }).start();\n}\n\nprivate String performN