لماذا تتعطل الملاحة في Android: IllegalArgumentException و NavController

مدرب بايثون أونلاين للمبتدئين

تعلم Python بسهولة دون إرهاق نظري. حل مهام عملية مع التحقق التلقائي واكتب الكود مباشرة في المتصفح.

ابدأ الدورة

«المسار مفقود»: لماذا يصاب Android Navigation Component بالجنون

\n\n

هل سبق لك أن شغّلت تطبيقك على Android، وانتقلت إلى شاشة، وضغطت على «رجوع» — وفجأة تعطل التطبيق مع ظهور الخطأ IllegalArgumentException: navigation destination xxx is unknown to this NavController؟ إذا كان الأمر كذلك — فأنت لست وحدك. هذا السؤال حصد آلاف المشاهدات على Stack Overflow، وكل مطور ثانٍ يبدأ العمل مع Jetpack Navigation يواجهه.

\n\n

في هذه المقالة، سنحلل لماذا يحدث IllegalArgumentException navigation destination unknown، وكيفية تشخيصه، والأهم من ذلك، كيفية إصلاحه. سيكون هناك الكثير من الأكواد الحية بلغة Kotlin، ونصائح عملية، ولا شيء من «الحشو». هيا بنا!

\n\n

ما هو NavController ولماذا «لا يعرف» وجهتك؟

\n\n

بدايةً — القليل من النظرية. NavController هو قائد الملاحة الرئيسي في Android. يدير الانتقالات بين الشاشات (fragments أو composables) بناءً على رسم بياني للملاحة — ملف XML يصف جميع المسارات الممكنة. عندما تستدعي navController.navigate(R.id.some_destination)، يبحث NavController عن هذا المعرف في رسمه البياني. إذا لم يجده — يرمي IllegalArgumentException.

\n\n

يبدو هذا منطقيًا، ولكن لماذا يحدث هذا في وقت التشغيل وليس أثناء التجميع؟ لأن الرسم البياني للملاحة يُحمّل ديناميكيًا، وغالبًا ما يرتبط الخطأ بأن الرسم البياني الحالي (الذي يحتوي على NavController) ببساطة لا يتضمن الوجهة المطلوبة.

\n\n

أكثر سيناريوهات الخطأ شيوعًا

\n
    \n
  • محاولة الانتقال إلى شاشة غير موصوفة في الرسم البياني الحالي — على سبيل المثال، تقوم بتبديل الأجزاء (fragments) بينما بقي NavController من الجزء السابق.
  • \n
  • استخدام الروابط العميقة (DeepLink) دون تسجيل صحيح — يتم تشغيل التطبيق عبر رابط، لكن الرسم البياني لم يُحمّل بعد.
  • \n
  • مسح مكدس الرجوع (back stack) ومحاولة الملاحة بعده — ينسى NavController مساراته.
  • \n
  • معرف وجهة غير صحيح — خطأ إملائي أو عدم تطابق الاسم في الكود وملف XML.
  • \n
\n\n

تحليل مثال واقعي (Kotlin)

\n\n

تخيل أن لديك تطبيقًا بشاشتين: HomeFragment و ProfileFragment. الرسم البياني للملاحة nav_graph.xml يبدو هكذا:

\n\n
<!-- 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، تقوم بالانتقال:

\n\n
// 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

لنفترض أنك أضفت زر «رجوع» في شريط الأدوات وكتبت الكود التالي:

\n\n
// MainActivity.kt\nsupportActionBar?.setDisplayHomeAsUpEnabled(true)\n\noverride fun onOptionsItemSelected(item: MenuItem): Boolean                

المدونات

توصيات الكتب