Global Exception Handler: الجوهر، مبدأ العمل والتنفيذ في المشاريع

المؤلف: IT Sectr نُشر: 2026-05-27 وقت القراءة: 8 دق

Global Exception Handler — آلية مركزية لالتقاط الاستثناءات غير المعالجة تمنع الإنهاء المفاجئ لتطبيق الجوال. وفقًا لـ Apple Developer، 2024، فإن المعالجة الصحيحة للاستثناءات تقلل عدد الأعطال بنسبة 40–60% وتحسن تجربة المستخدم. بدون مثل هذا المعالج، أي استثناء غير معالج في خلفية يؤدي إلى إغلاق فوري للتطبيق.

الرئيسية

  • Global Exception Handler — نقطة تجميع مركزية لجميع الاستثناءات غير المعالجة في التطبيق، تمنع الأعطال
  • iOS NSSetUncaughtExceptionHandler — دالة C لاعتراض استثناءات Objective-C على منصة Apple
  • Android Thread.setDefaultUncaughtExceptionHandler — آلية مدمجة في المنصة للالتقاط العالمي للاستثناءات
  • تسجيل قبل الإغلاق — المهمة الرئيسية للمعالج: حفظ معلومات العطل قبل إنهاء العملية
  • التدهور التدريجي — يسمح المعالج بعرض شاشة خطأ مناسبة للمستخدم بدلاً من الإغلاق المفاجئ

ما هو Global Exception Handler؟

Global Exception Handler — آلية مركزية لالتقاط الاستثناءات التي لم يتم معالجتها على مستوى الوظائف أو الوحدات الفردية للتطبيق. في سياق تطوير التطبيقات المحمولة، يعمل هذا المعالج كخط الدفاع الأخير قبل الإنهاء غير الطبيعي للعملية.

توفر iOS وAndroid واجهات برمجة تطبيقات مدمجة لتعيين معالج عالمي. تستخدم Apple NSSetUncaughtExceptionHandler لبيئة Objective-C، بينما تقدم Google Thread.setDefaultUncaughtExceptionHandler في Java/Kotlin. تلتقط كلتا الآليتين الاستثناءات التي لم يتم التقاطها بواسطة بنيات try-catch في جميع خيوط التطبيق.

وفقًا لـ Crashlytics (Google، 2024)، حوالي 25% من الأعطال تحدث بسبب استثناءات غير معالجة في الخيوط الخلفية — وهي منطقة يكون فيها Global Exception Handler بالغ الأهمية. غالبًا ما يركز المطورون على خيط واجهة المستخدم، متناسين العمليات غير المتزامنة.

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

كيف يعمل المعالج العالمي للاستثناءات

آلية العمل لـ Global Exception Handler تعتمد على اعتراض إشارات نظام التشغيل أو استثناءات وقت التشغيل. عندما يطرح الكود استثناءً لم يتم التقاطه بواسطة أي كتلة try-catch، يتم نقل التحكم إلى معالج مُسجل مسبقًا.

على iOS، يتم تسجيل المعالج عبر NSSetUncaughtExceptionHandler ويستقبل كائن NSException مع تتبع كامل للمكدس. على Android، يُستخدم Thread.setDefaultUncaughtExceptionHandler الذي يقبل Thread و Throwable — مما يوفر الوصول إلى نوع الاستثناء والرسالة ومكدس الاستدعاءات.

بعد تلقي بيانات العطل، يقوم المعالج بثلاثة إجراءات إلزامية: كتابة السجل في التخزين المحلي، إرسال تقرير إلى Crashlytics أو Sentry، وإنهاء التطبيق بشكل صحيح. وفقًا لـ Apple WWDC 2023، وقت تشغيل المعالج محدود بـ 5 ثوانٍ — بعد ذلك ينهي النظام العملية قسرًا.

لتطبيقات Swift بدءًا من iOS 13، تم تقديم Signals API الذي يعالج ليس فقط الاستثناءات بل أيضًا إشارات نظام التشغيل — SIGABRT، SIGSEGV، SIGBUS، مما يوسع تغطية المعالج لتشمل أخطاء الذاكرة منخفضة المستوى.

تنفيذ Global Exception Handler على iOS

التنفيذ لمعالج عالمي على iOS يتطلب تعيين دالة C عبر NSSetUncaughtExceptionHandler. يتم استدعاء المعالج بشكل متزامن لحظة استثناء غير معالج ويتلقى سياق الخطأ الكامل.

objective-c
void handleUncaughtException(NSException exception) {
    NSDictionary userInfo = [exception userInfo];
    NSArray stackTrace = [exception callStackSymbols];
    NSString reason = [exception reason];

    // حفظ سجل العطل في ملف محلي
    NSString logPath = [NSSearchPathForDirectoriesInDomains(
        NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
    [exceptionLog writeToFile:logPath atomically:YES];
}

int main(int argc, char argv[]) {
    NSSetUncaughtExceptionHandler(&handleUncaughtException);
    return UIApplicationMain(argc, argv, nil, nil);
}

ميزة مهمة لتنفيذ iOS: المعالج يلتقط فقط استثناءات Objective-C. أخطاء Swift التي تستخدم آلية throw-catch لا تصل إلى هذا المعالج — فهي تتطلب معالجة منفصلة عبر Swift Error Handling. بدءًا من iOS 14، توصي Apple بدمج NSSetUncaughtExceptionHandler مع Signals API للحصول على أقصى تغطية.

وفقًا لـ Apple Technical Note TN2151، بعد استدعاء المعالج، يجب إنهاء التطبيق في غضون 5 ثوانٍ. أي محاولة لمواصلة التنفيذ بعد العودة من المعالج تؤدي إلى سلوك غير محدد وعطل متكرر.

تنفيذ Global Exception Handler على Android

Android يوفر آلية أكثر مرونة للمعالجة العالمية للاستثناءات عبر Thread.setDefaultUncaughtExceptionHandler. يتلقى المعالج مرجعًا للخيط الذي حدث فيه الاستثناء وكائن Throwable نفسه.

kotlin
class GlobalExceptionHandler : Thread.UncaughtExceptionHandler {

    override fun uncaughtException(thread: Thread, throwable: Throwable) {
        // حفظ سجل العطل في ملف
        val stackTrace = throwable.stackTraceToString()
        val crashLog = "CRASH: ${thread.name}\n${stackTrace}"

        val file = File(context.cacheDir, "crash_log.txt")
        file.writeText(crashLog)

        // إرسال إلى Crashlytics
        FirebaseCrashlytics.getInstance()
            .recordException(throwable)

        // إنهاء العملية
        android.os.Process.killProcess(
            android.os.Process.myPid()
        )
    }
}

// الإعداد في Application.onCreate
class App : Application() {
    override fun onCreate() {
        super.onCreate()
        Thread.setDefaultUncaughtExceptionHandler(
            GlobalExceptionHandler()
        )
    }
}

فرق رئيسي لتنفيذ Android: كل خيط له معالجه الخاص، ويقوم setDefaultUncaughtExceptionHandler بتعيين المعالج لجميع الخيوط التي ليس لها معالج فردي مخصص. هذا يضمن تغطية عالمية — من خيط واجهة المستخدم إلى AsyncTask الخلفية و coroutines.

على Android 12+، هناك قيود: بعد استدعاء uncaughtException، يجب إنهاء التطبيق في غضون 100 مللي ثانية. إذا قام المعالج بتنفيذ عمليات طويلة، فقد يقتل النظام العملية قبل كتابة السجل. يوصى باستخدام خدمة خلفية لإرسال تقارير الأعطال.

أفضل الممارسات مع Global Exception Handler

القاعدة الأولى — لا تحاول استعادة تشغيل التطبيق بعد استثناء غير معالج. حالة التطبيق بعد العطل غير محددة، وقد يؤدي استمرار التنفيذ إلى تلف بيانات المستخدم.

تقليل وقت تشغيل المعالج

الحد الزمني — القيد التقني الرئيسي لـ Global Exception Handler. على iOS هو 5 ثوانٍ، على Android — 100 مللي ثانية. داخل المعالج، يمكن فقط حفظ مجموعة بيانات دنيا: نوع الاستثناء، مكدس الاستدعاءات وحالة بعض المتغيرات الرئيسية.

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

الدمج مع أنظمة الإبلاغ عن الأعطال

خدمات الإبلاغ عن الأعطال — Firebase Crashlytics، Sentry، Bugsnag — تقوم بتعيين معالج عالمي خاص بها. إذا قام المطور بتعيين معالج مخصص إضافي، يجب نقل التحكم إلى نظام الإبلاغ عن الأعطال بعد إجراءاته الخاصة. على Android يُستخدم تكوين المعالجات: تنفيذ المنطق الخاص، ثم استدعاء المعالج السابق.

لـ Firebase Crashlytics، يُوصى بعدم تعيين Thread.setDefaultUncaughtExceptionHandler مخصص على الإطلاق — SDK Crashlytics يفعل ذلك تلقائيًا عند التهيئة.

تسجيل معلومات إضافية

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

على iOS، يمكن استخدام NSSetUncaughtExceptionHandler ليس فقط للكتابة ولكن أيضًا للتخزين المؤقت في NSUserDefaults مع علامة synchronize — مما يضمن الاستمرارية حتى عند الإنهاء الفوري للعملية.

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

اختبار إلزامي — يجب اختبار Global Exception Handler في كل مرحلة من CI/CD. على iOS يمكن بدء استثناء اختباري عبر @throw NSException، على Android — عبر throw RuntimeException(). يتم التحقق من استدعاء المعالج وحفظ السجل وإنهاء التطبيق بشكل صحيح.

وفقًا لـ Google I/O 2023، أكثر من 30% من الأعطال في الإنتاج تحدث على أجهزة لم يختبرها المطور — إصدارات Android مختلفة، برامج ثابتة مخصصة، ذاكرة محدودة.

الأخطاء الشائعة عند استخدام المعالج

الخطأ الأول والأكثر شيوعًا — محاولة مواصلة تنفيذ التطبيق بعد معالجة الاستثناء. بعد استدعاء uncaughtException، يكون التطبيق في حالة غير مستقرة، وأي عمليات إضافية قد تسبب أخطاء متتالية وتلف البيانات.

الخطأ الثاني — تنفيذ عمليات طويلة داخل المعالج. طلبات الشبكة وكتابة ملفات كبيرة أو حسابات معقدة لا تكتمل قبل الإنهاء القسري للعملية. وفقًا لـ Apple Technical Q&A QA1468، محاولة إرسال طلب HTTP داخل المعالج هي السبب الرئيسي لفقدان تقارير الأعطال.

الخطأ الثالث — تجاهل الخيوط الخلفية. Global Exception Handler المُعيّن فقط للخيط الرئيسي لا يحمي من الأعطال في coroutines وDispatchQueue وAsyncTask وRxJava. على Android، كل خيط يجب أن يكون له معالجه الخاص — و setDefaultUncaughtExceptionHandler يحل هذا فقط للخيوط بدون معالج فردي.

الخطأ الرابع — عدم وجود خطة بديلة لإشارات نظام التشغيل. NSSetUncaughtExceptionHandler على iOS لا يلتقط SIGABRT وSIGSEGV وSIGBUS. هذه الإشارات تتطلب إعداد معالجات منفصلة عبر sigaction API. يكتشف المطورون هذا فقط عندما يتعطل التطبيق دون أي تقرير عطل واحد.

الخطأ الخامس — تسجيل بيانات سرية. قد تظهر في سجل العطل رسائل البريد الإلكتروني ورموز التفويض أو البيانات الشخصية للمستخدمين. هذا ينتهك GDPR و إرشادات مراجعة متجر تطبيقات Apple. قم دائمًا بتصفية البيانات المنقولة عبر تعبير عادي أو قائمة بيضاء للحقول المسموح بها.

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

هل يمكن استعادة تشغيل التطبيق بعد استثناء عالمي؟

لا — بعد استدعاء Global Exception Handler، حالة التطبيق غير محددة. أي محاولة لمواصلة التشغيل قد تؤدي إلى تلف البيانات. الإجراء الصحيح الوحيد هو حفظ سجل العطل وإنهاء العملية.

هل يلتقط Global Exception Handler جميع أنواع الأخطاء؟

ليس الكل — على iOS، يلتقط NSSetUncaughtExceptionHandler فقط استثناءات Objective-C. أخطاء Swift وإشارات نظام التشغيل (SIGSEGV، SIGABRT) تتطلب معالجات منفصلة. على Android، يلتقط Thread.setDefaultUncaughtExceptionHandler جميع RuntimeExceptions ولكن ليس أخطاء الكود الأصلي عبر JNI.

كيف أنقل التحكم إلى نظام الإبلاغ عن الأعطال بعد معالجي؟

احفظ مرجعًا إلى المعالج السابق عبر Thread.getDefaultUncaughtExceptionHandler() قبل تعيين معالجك. في نهاية معالجك، استدع previousHandler.uncaughtException(thread, throwable) — هذا يضمن أن Crashlytics أو Sentry يستقبلان بياناتهما.

ماذا أفعل إذا حدث العطل في كود أصلي C/C++؟

للكود الأصلي، مطلوب معالجة الإشارات عبر sigaction() — SIGSEGV، SIGABRT، SIGBUS. على Android يمكن استخدام Google Breakpad أو Crashpad. على iOS بدءًا من الإصدار 13، تتوفر Signals API لمعالجة استثناءات mach.

هل يمكن أن يؤثر Global Exception Handler على الأداء؟

لا — تعيين المعالج يؤثر فقط على لحظة حدوث الاستثناء. في التشغيل الطبيعي للتطبيق، لا يوجد حمل إضافي. الخطر الوحيد هو تسرب الذاكرة إذا كان المعالج يحتفظ بمرجع إلى Activity أو Context، مما يمنع جمع القمامة.

الخلاصة

  • Global Exception Handler — خط الدفاع الأخير قبل العطل، إلزامي في أي تطبيق إنتاجي
  • iOS NSSetUncaughtExceptionHandler يلتقط استثناءات Objective-C مع حد معالجة 5 ثوانٍ
  • Android Thread.setDefaultUncaughtExceptionHandler يعمل لجميع الخيوط بدون معالج شخصي
  • وقت تشغيل المعالج يجب أن يكون ضئيلاً — حفظ البيانات وإنهاء العملية دون محاولة استعادة
  • إشارات نظام التشغيل (SIGSEGV، SIGABRT) لا تلتقطها المعالجات القياسية — يتطلب sigaction API
  • أنظمة الإبلاغ عن الأعطال يجب استدعاؤها عبر تكوين المعالجات، نقل التحكم بعد منطقك
  • اختبار المعالج في CI/CD — مرحلة إلزامية تمنع فقدان تقارير الأعطال في الإنتاج

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

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

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

اقرأ أيضًا