«المسار مفقود»: لماذا يصاب Android Navigation Component بالجنون
\n\nهل سبق لك أن شغّلت تطبيقك على Android، وانتقلت إلى شاشة، وضغطت على «رجوع» — وفجأة تعطل التطبيق مع ظهور الخطأ IllegalArgumentException: navigation destination xxx is unknown to this NavController؟ إذا كان الأمر كذلك — فأنت لست وحدك. هذا السؤال حصد آلاف المشاهدات على Stack Overflow، وكل مطور ثانٍ يبدأ العمل مع Jetpack Navigation يواجهه.
في هذه المقالة، سنحلل لماذا يحدث IllegalArgumentException navigation destination unknown، وكيفية تشخيصه، والأهم من ذلك، كيفية إصلاحه. سيكون هناك الكثير من الأكواد الحية بلغة Kotlin، ونصائح عملية، ولا شيء من «الحشو». هيا بنا!
\n\nما هو NavController ولماذا «لا يعرف» وجهتك؟
\n\nبدايةً — القليل من النظرية. NavController هو قائد الملاحة الرئيسي في Android. يدير الانتقالات بين الشاشات (fragments أو composables) بناءً على رسم بياني للملاحة — ملف XML يصف جميع المسارات الممكنة. عندما تستدعي navController.navigate(R.id.some_destination)، يبحث NavController عن هذا المعرف في رسمه البياني. إذا لم يجده — يرمي IllegalArgumentException.
يبدو هذا منطقيًا، ولكن لماذا يحدث هذا في وقت التشغيل وليس أثناء التجميع؟ لأن الرسم البياني للملاحة يُحمّل ديناميكيًا، وغالبًا ما يرتبط الخطأ بأن الرسم البياني الحالي (الذي يحتوي على NavController) ببساطة لا يتضمن الوجهة المطلوبة.
\n\nأكثر سيناريوهات الخطأ شيوعًا
\n- \n
- محاولة الانتقال إلى شاشة غير موصوفة في الرسم البياني الحالي — على سبيل المثال، تقوم بتبديل الأجزاء (fragments) بينما بقي NavController من الجزء السابق. \n
- استخدام الروابط العميقة (DeepLink) دون تسجيل صحيح — يتم تشغيل التطبيق عبر رابط، لكن الرسم البياني لم يُحمّل بعد. \n
- مسح مكدس الرجوع (back stack) ومحاولة الملاحة بعده — ينسى NavController مساراته. \n
- معرف وجهة غير صحيح — خطأ إملائي أو عدم تطابق الاسم في الكود وملف XML. \n
تحليل مثال واقعي (Kotlin)
\n\nتخيل أن لديك تطبيقًا بشاشتين: HomeFragment و ProfileFragment. الرسم البياني للملاحة nav_graph.xml يبدو هكذا:
<!-- nav_graph.xml -->\n<navigation xmlns:android="http://schemas.android.com/apk/res/android"\n xmlns:app="http://schemas.android.com/apk/res-auto"\n android:id="@+id/nav_graph"\n app:startDestination="@id/homeFragment">\n\n <fragment\n android:id="@+id/homeFragment"\n android:name="com.example.app.HomeFragment"\n android:label="Home">\n <action\n android:id="@+id/action_home_to_profile"\n app:destination="@id/profileFragment" />\n </fragment>\n\n <fragment\n android:id="@+id/profileFragment"\n android:name="com.example.app.ProfileFragment"\n android:label="Profile" />\n</navigation>\n\nفي HomeFragment، تقوم بالانتقال:
// HomeFragment.kt\nval navController = findNavController()\nnavController.navigate(R.id.action_home_to_profile)\n\nكل شيء يعمل. ولكن ماذا لو حاولت استدعاء navController.navigate(R.id.profileFragment) من سياق آخر — على سبيل المثال، من Activity أو BroadcastReceiver؟ أو إذا قمت بإعادة تحميل الرسم البياني؟ هنا يظهر IllegalArgumentException: navigation destination xxx is unknown to this NavController.
إعادة إنتاج الخطأ
\n\nلنفترض أنك أضفت زر «رجوع» في شريط الأدوات وكتبت الكود التالي:
\n\n// MainActivity.kt\nsupportActionBar?.setDisplayHomeAsUpEnabled(true)\n\noverride fun onOptionsItemSelected(item: MenuItem): Boolean