Crash في تطوير التطبيقات المحمولة: ما هو، أنواعه وطرق منعه

المؤلف: IT Sectr نُشر: 2026-03-29 وقت القراءة: 9 دق

Crash هو إنهاء غير طبيعي لتطبيق محمول بسبب استثناء غير معالج أو عطل نظامي fatal. وفقًا لـ Firebase Crashlytics، حوالي 2% من المستخدمين يواجهون الأعطال يوميًا، وكل عطل يقلل الاحتفاظ بالمستخدمين بنسبة 10–20%. فهم أسباب وطرق منع الأعطال هو مهارة أساسية لأي مطور تطبيقات محمولة.

الخلاصة

  • Crash — استثناء غير معالج يؤدي إلى إنهاء غير طبيعي للعملية
  • NullPointerException — أكثر أنواع الأعطال شيوعًا في تطبيقات Java/Kotlin
  • أدوات الإبلاغ عن الأعطال تجمع stack trace وحالة الجهاز وبيانات المستخدم
  • Firebase Crashlytics — الأداة القياسية لمراقبة الأعطال في تطوير التطبيقات المحمولة
  • المنع يشمل معالجة الأخطاء بشكل صحيح والاختبار والتحقق من السلامة من القيم الفارغة

ما هو Crash

Crash هو إنهاء غير طبيعي للتطبيق بسبب استثناء غير معالج أو إشارة نظام fatal لم يتم معالجتها في كود التطبيق. عندما يكتشف النظام أو الآلة الافتراضية (JVM, ART) حالة fatal — NullPointerException, IndexOutOfBoundsException, OutOfMemoryError — يقوم فورًا بإيقاف العملية وتفريغها من الذاكرة. يرى المستخدم إغلاقًا مفاجئًا للتطبيق دون أي إشعار نظامي بالخطأ. وفقًا لـ Google، التطبيقات التي يقل معدل خلوها من الأعطال عن 99% تفقد ما يصل إلى 20% من المستخدمين النشطين شهريًا.

في Android، تختلف آلية معالجة الأعطال عن أنظمة سطح المكتب. بدلاً من حوار تصحيح مع stack trace، يقوم Android ببساطة بإنهاء العملية دون حفظ معلومات مفصلة. جمع معلومات العطل هو مهمة المكتبات الخارجية (Crashlytics, Sentry, Bugsnag) التي تعترض الاستثناءات عبر Thread.setDefaultUncaughtExceptionHandler قبل إنهاء العملية.

iOS يستخدم آلية مشابهة مع NSException و Mach exceptions لمعالجة الأخطاء fatal. عند حدوث استثناء غير معالج، ينهي النظام التطبيق، ويُحفظ التقرير كملف .crash. يتطلب جمع الأعطال على iOS التكامل مع Crashlytics أو التقرير المدمج عبر Xcode Organizer.

الأنواع الرئيسية للأعطال

خمس فئات من الأعطال تغطي 90% من جميع حالات الفشل في التطبيقات المحمولة. يساعد فهم كل نوع على تشخيص المشكلات وإصلاحها في الإنتاج بشكل أسرع.

NullPointerException — ملك الأعطال

NullPointerException (NPE) هو أكثر أنواع الأعطال شيوعًا في جميع تطبيقات Java/Kotlin. يحدث عند محاولة استدعاء دالة أو الوصول إلى حقل لكائن قيمته null. السيناريوهات النموذجية: حقل Activity غير مهيأ عند تدوير الشاشة، استجابة null من الخادم أثناء إلغاء تسلسل JSON، تنقل غير دقيق عبر محول RecyclerView.

Kotlin يحل مشكلة NPE على مستوى اللغة من خلال أنواع آمنة من القيم الفارغة: String? لا يمكن استخدامه دون فحص صريح. ومع ذلك، فإن التوافق مع Java و Reflection لا يزالان يخلقان مخاطر. استخدم التعليقات التوضيحية @NonNull و @Nullable وفعّل strictNullChecks في أدوات التحليل الثابت.

kotlin
fun safeLength(text: String?): Int {
    return text?.length ?: 0 // معالجة آمنة للقيم الفارغة null
}

IndexOutOfBoundsException وأخطاء المجموعات

IndexOutOfBoundsException يحدث عند الوصول إلى فهرس غير موجود في قائمة أو مصفوفة. السيناريوهات الشائعة: إزالة عنصر من RecyclerView دون مزامنة مع المحول، تعديل ArrayList متعدد الخيوط دون قفل، حساب غير صحيح للموضع في ViewPager. ConcurrentModificationException قريب له عند التكرار وتعديل المجموعات في وقت واحد.

استخدم CopyOnWriteArrayList للوصول متعدد الخيوط أو المجموعات الخالية من القفل من java.util.concurrent. لمزامنة واجهة المستخدم، استخدم DiffUtil الذي يحسب الفرق بين القائمتين القديمة والجديدة بأمان وكفاءة.

ClassCastException — مشاكل الأنواع

ClassCastException يحدث عند تحويل كائن إلى نوع غير متوافق. في Android، الأسباب النموذجية: نوع ViewHolder غير صحيح في RecyclerView (أنواع خلايا مختلفة بدون getItemViewType مناسب)، تحويل Fragment غير صحيح أثناء التنقل، كائنات Serializable بإصدارات فئات مختلفة.

استخدم التحويل الآمن في Kotlin عبر عامل التشغيل as?، الذي يعيد null عند عدم توافق الأنواع. في Java — تحقق عبر instanceof قبل التحويل. بالنسبة لكائنات Parcelable، أعلن دائمًا عن CREATOR في كل فئة.

IllegalStateException والأخطاء المنطقية

IllegalStateException يشير إلى استدعاء دالة في حالة غير مناسبة للكائن. مثال نموذجي في Android — getSupportFragmentManager() بعد onSaveInstanceState، عندما لا يُسمح بـ commit() لـ fragment. حالة شائعة أخرى — استدعاء dismiss() على حوار مغلق بالفعل.

تحقق من حالة دورة الحياة قبل عمليات FragmentManager. استخدم commitAllowingStateLoss() فقط عندما تكون متأكدًا من أن فقدان الحالة ليس حرجًا. في Kotlin، أنشئ منشئات شبيهة بـ DSL تستبعد الحالات غير الصالحة على مستوى الأنواع.

Native Crash (إشارات SIGSEGV, SIGABRT)

Native Crash يحدث في كود C/C++ الأصلي بسبب انتهاكات الذاكرة: إلغاء مرجع لمؤشر فارغ، double-free، تجاوز سعة المخزن المؤقت للمكدس. في Android، تحدث هذه الأعطال في مكتبات NDK ومحركات الألعاب (Unity, Unreal) والتبعيات النظامية. Native Crash لا يتم اعتراضه بواسطة Thread.setDefaultUncaughtExceptionHandler — فهو ينهي العملية فورًا.

لتشخيص الأعطال الأصلية، استخدم ملفات minidump (Breakpad) أو tombstones في Android. يدعم Firebase Crashlytics جمع الأعطال الأصلية عبر NDK SDK. على iOS، يتم حل مشكلة مماثلة باستخدام PLCrashReporter.

أدوات الإبلاغ عن الأعطال

ثلاث أدوات تهيمن على سوق الإبلاغ عن الأعطال في التطبيقات المحمولة. كل منها يوفر جمع stack trace، وتجميع حسب إصدار التطبيق، وإشعارات بالأعطال الجديدة.

Firebase Crashlytics

Crashlytics هي أداة الإبلاغ عن الأعطال الأكثر شيوعًا للتطبيقات المحمولة، وهي جزء من نظام Firebase البيئي. تجمع تلقائيًا stack traces ومعلومات الجهاز وإصدار نظام التشغيل والمفاتيح المخصصة للمستخدم. يستغرق التكامل 10 دقائق عبر Firebase Console و Gradle Plugin. تدعم Crashlytics أيضًا السجلات في الوقت الفعلي (Logcat) وتتبعات المستخدم.

kotlin
FirebaseCrashlytics.getInstance()
    .setCustomKey("current_screen", "ProfileFragment")

FirebaseCrashlytics.getInstance()
    .log("User tapped login button")

Sentry

Sentry هو بديل لـ Crashlytics بنظام تصفية أكثر مرونة ودعم لأكثر من 90 منصة. على عكس Firebase، يوفر Sentry خادمًا مستضافًا ذاتيًا (self-hosted) للشركات ذات المتطلبات الصارمة للبيانات. يدعم Sentry التتبع التوزيعي و breadcrumbs والتكامل مع خطوط أنابيب CI/CD.

Bugsnag و AppCenter

Bugsnag يتميز بدعم التنبيهات القائمة على الشدة: يصنف الأعطال إلى critical و error و warning. AppCenter من Microsoft هي أداة مجانية بوظائف أساسية للمشاريع الصغيرة. كلاهما يدعم Android و iOS و React Native و Flutter.

كيفية تحليل العطل

تحليل العطل هو عملية إعادة بناء الصورة الكاملة لما حدث. يظهر stack trace فقط نقطة الفشل الأخيرة ولكنه لا يوفر السياق الذي أدى إلى المشكلة. يتضمن النهج الاحترافي أربع مراحل.

المرحلة الأولى — قراءة stack trace. حدد الفئة والدالة وسطر الكود حيث حدث الاستثناء. تتبع سلسلة الاستدعاءات من الإطار العلوي إلى السفلي: آخر سطر في المكدس هو موقع العطل، والأسطر العلوية هي تسلسل الاستدعاءات. إزالة التعتيم (خريطة ProGuard/R8) إلزامية لإصدارات الإنتاج.

المرحلة الثانية — سياق الجهاز. يعرض Crashlytics طراز الجهاز وإصدار نظام التشغيل والذاكرة المتاحة وإصدار التطبيق. على سبيل المثال، عطل يحدث فقط على Samsung Galaxy S10 مع Android 11 يشير إلى مشكلة في إصدار معين من One UI، وليس خطأ عام في الكود.

المرحلة الثالثة — إعادة الإنتاج على جهاز اختبار. إذا لم يتكرر العطل بشكل مستقر، اسأل المستخدم عن الخطوات الدقيقة أو استخدم Remote Config لتسجيل الدخول قبل قسم الكود المشكل. اختبار AB للإصلاح على جزء من الجمهور يساعد في تأكيد الحل.

المرحلة الرابعة — المراقبة بعد الإصلاح. بعد نشر الإصلاح، راقب معدل العطل لمدة 3–5 أيام. إذا اختفى العطل تمامًا — نجح الإصلاح. إذا انخفض التكرار لكنه لم يصل إلى الصفر — يوجد سيناريو ثانٍ يتطلب تحليلًا منفصلًا.

ممارسات منع الأعطال

نهج منهجي لمنع الأعطال يشمل أدوات التحليل الثابت والاختبار الإلزامي للحالات الحدية ومعالجة الأخطاء بشكل صحيح على جميع مستويات التطبيق.

التحليل الثابت للكود

Detekt (Kotlin) و Lint (Android) يجدان المشكلات المحتملة في وقت الترجمة: المتغيرات غير المستخدمة، احتمالات NPE، استخدام غير صحيح لـ API. قم بتضمين هذه الأدوات في خط أنابيب CI مع حد للأخطاء. على سبيل المثال، Detekt مع تهيئة 30+ تحذيرًا أو أي خطأ مانع لا يمرر البناء.

اختبارات الوحدة واختبارات واجهة المستخدم

تغطية السيناريوهات الرئيسية للاستخدام باختبارات الوحدة هي الحماية الأساسية ضد أعطال الانحدار. اختبر نماذج البيانات و ViewModel وطبقات UseCase مع الحالات الحدية: قيم null وقوائم فارغة و JSON غير صالح. تغطي اختبارات واجهة المستخدم عبر Espresso أو Compose Test التدفقات الحرجة: المصادقة والدفع والتوجيه.

التدهور التدريجي

صمم التطبيق بحيث لا يؤدي فشل في وحدة واحدة إلى تحطيم الشاشة بأكملها. استخدم كتل catch على مستوى ViewModel مع حالة احتياطية: إظهار عنصر نائب بدلاً من قائمة، بيانات مخبأة عند عدم وجود اتصال، صورة احتياطية عند خطأ التحميل. هذا يحول العطل المحتمل إلى سيناريو UX متحكم فيه.

الإصدار التدريجي مع المراقبة

الإصدارات المرحلية هي ممارسة قياسية في Google Play و App Store: يتم توزيع إصدار جديد على 5%، ثم 20%، ثم 100% من الجمهور بفاصل 1–3 أيام. في كل مرحلة، يتم مراقبة معدل الأعطال: إذا انخفض معدل الخلو من الأعطال عن 99.5%، يتوقف الإصدار تلقائيًا. يتيح Firebase Remote Config تعطيل الميزات المشكلة دون نشر إصدار جديد.

التحكم في إصدارات التبعيات

Renovate أو Dependabot في CI يتحققان تلقائيًا من المكتبات بحثًا عن ثغرات معروفة وأخطاء حرجة. تحديث تبعية واحدة قد يزيل فئة كاملة من الأعطال. ومع ذلك، اختبر التحديثات على بيئة staging قبل الإصدار إلى الإنتاج — قد يحتوي إصدار المكتبة الجديد على تغييرات غير متوافقة.

الأسئلة الشائعة

هل يمكن منع 100% من الأعطال؟

لا. بعض الأعطال تسببها عوامل خارجة عن سيطرة المطور: أخطاء النظام، مشاكل الأجهزة، عدم توافق البرامج الثابتة. الهدف هو تقليل المعدل إلى 0.1% أو أقل وتقليل وقت الاستجابة للأعطال المتبقية.

ما الفرق بين أداة الإبلاغ عن الأعطال والتحليلات؟

أداة الإبلاغ عن الأعطال تجمع stack trace وحالة الذاكرة ومعلومات الجهاز عند لحظة العطل. التحليلات تجمع بيانات سلوك المستخدم. Crashlytics تجمع بين كلا النهجين، وتوفر سياق العطل مع المفاتيح المخصصة للمستخدم.

لماذا stack trace مشوش؟

ProGuard و R8 يشوشان الكود لحماية الملكية الفكرية. لإزالة التشويش، قم بتحميل ملف mapping إلى Crashlytics أثناء النشر. بدون ملف mapping، سيعرض stack trace a.a(), b.b() بدلاً من أسماء الفئات والدوال الحقيقية.

كيف تعترض أداة الإبلاغ عن الأعطال الاستثناءات؟

عبر Thread.setDefaultUncaughtExceptionHandler على Android: تسجل المكتبة معالجها الخاص الذي يستقبل الاستثناء غير المعالج أولاً، ويحفظ البيانات، وعندها فقط ينهي العملية. على iOS، يُستخدم NSSetUncaughtExceptionHandler لـ NSException و Mach exception handler للإشارات.

ما هو العطل fatal وغير fatal؟

Fatal — تم إنهاء التطبيق. غير fatal (استثناء تم捕获ه) — المطور التقط الاستثناء عبر try-catch، لكنه قد يشير إلى مشكلة محتملة. تميز Crashlytics بين هذه الأنواع وتسمح بتصفية غير fatal بشكل منفصل لتجنب ازدحام لوحة البيانات.

الملخص

  • Crash — إنهاء غير طبيعي للتطبيق بسبب استثناء غير معالج أو إشارة fatal
  • NullPointerException يبقى أكثر أنواع الأعطال شيوعًا في التطبيقات المحمولة
  • Firebase Crashlytics — الأداة القياسية لجمع وتحليل الأعطال في الإنتاج
  • تحليل العطل يشمل قراءة stack trace وسياق الجهاز وإعادة الإنتاج في بيئة اختبار
  • التحليل الثابت (Detekt, Lint) يمنع بعض الأعطال في وقت الترجمة
  • التدهور التدريجي يحول الأعطال المحتملة إلى سيناريوهات قابلة للإدارة ببيانات احتياطية
  • ملفات mapping إلزامية لإزالة تشويش stack trace في إصدارات الإنتاج

سنقوم بتطوير تطبيق جوال جاهز

تقدم IT Sectr تطبيقات iOS وAndroid للشركات الناشئة والشركات منذ عام 2017. سوف نقدم لك النصح ونقترح أفضل حل.

مناقشة المشروع

اقرأ أيضًا