Global Exception Handler: ماهیت، اصل کار و پیاده‌سازی در پروژه‌ها

نویسنده: IT Sectr منتشر شده: 2026-05-27 زمان مطالعه: 8 دقیقه

Global Exception Handler — مکانیزم متمرکز رهگیری استثناهای مدیریت‌نشده که از بسته شدن ناگهانی برنامه موبایل جلوگیری می‌کند. طبق داده‌های Apple Developer, 2024، مدیریت صحیح استثناها تعداد crashها را 40–60% کاهش می‌دهد و تجربه کاربری را بهبود می‌بخشد. بدون چنین handlerای، هر استثنای مدیریت‌نشده در نخ پس‌زمینه منجر به بسته شدن فوری برنامه می‌شود.

نکات اصلی

  • Global Exception Handler — نقطه جمع‌آوری تمام استثناهای مدیریت‌نشده در برنامه، جلوگیری از crash
  • iOS NSSetUncaughtExceptionHandler — تابع C برای رهگیری استثناهای Objective-C در پلتفرم اپل
  • Android Thread.setDefaultUncaughtExceptionHandler — مکانیزم داخلی پلتفرم برای رهگیری سراسری استثناها
  • ثبت لاگ قبل از بسته شدن — وظیفه اصلی handler: ذخیره اطلاعات crash قبل از پایان فرآیند
  • Graceful degradation — handler به کاربر امکان نمایش صفحه خطای مناسب را به جای بسته شدن ناگهانی می‌دهد

Global Exception Handler چیست؟

Global Exception Handler — یک مکانیزم متمرکز برای رهگیری استثناهایی است که در سطح توابع یا ماژول‌های جداگانه برنامه مدیریت نشده‌اند. در زمینه توسعه موبایل، چنین handlerای به عنوان آخرین خط دفاعی قبل از پایان اجباری فرآیند عمل می‌کند.

iOS و Android APIهای داخلی برای نصب handler سراسری ارائه می‌دهند. اپل از NSSetUncaughtExceptionHandler برای محیط Objective-C استفاده می‌کند و گوگل Thread.setDefaultUncaughtExceptionHandler را در Java/Kotlin ارائه می‌دهد. هر دو مکانیزم استثناهایی را رهگیری می‌کنند که توسط ساختارهای try-catch در تمام نخ‌های برنامه گرفته نشده‌اند.

طبق داده‌های Crashlytics (Google, 2024)، حدود 25% از crashها به دلیل استثناهای مدیریت‌نشده در نخ‌های پس‌زمینه رخ می‌دهند — منطقه‌ای که Global Exception Handler در آن بسیار حیاتی است. توسعه‌دهندگان اغلب روی نخ UI تمرکز می‌کنند و عملیات ناهمگام را فراموش می‌کنند.

استفاده از handler سراسری جایگزین مدیریت محلی خطاها نمی‌شود، بلکه آن را تکمیل می‌کند. وظیفه اصلی — ذخیره حداکثر اطلاعات درباره وضعیت برنامه در لحظه استثنا و پایان صحیح کار است.

چگونه handler سراسری استثناها کار می‌کند

مکانیزم کار Global Exception Handler بر اساس رهگیری سیگنال‌های سیستم عامل یا استثناهای زمان اجرا است. وقتی کد استثنایی را پرتاب می‌کند که توسط هیچ بلوک try-catch گرفته نشده، کنترل به handler از پیش تعیین‌شده منتقل می‌شود.

در پلتفرم iOS، handler از طریق NSSetUncaughtExceptionHandler ثبت می‌شود و شیء NSException را با stack trace کامل دریافت می‌کند. در Android از Thread.setDefaultUncaughtExceptionHandler استفاده می‌شود که Thread و Throwable را دریافت می‌کند — این امکان دسترسی به نوع استثنا، پیام و پشته فراخوانی را فراهم می‌کند.

پس از دریافت داده‌های crash، handler سه اقدام اجباری را انجام می‌دهد: نوشتن لاگ در حافظه محلی، ارسال گزارش به Crashlytics یا Sentry و پایان صحیح برنامه. طبق داده‌های Apple WWDC 2023، زمان کار handler به 5 ثانیه محدود است — پس از آن سیستم به اجبار فرآیند را خاتمه می‌دهد.

برای برنامه‌های Swift از iOS 13 به بعد، Signals API ظاهر شد که نه تنها استثناها، بلکه سیگنال‌های سیستم عامل — SIGABRT، SIGSEGV و SIGBUS را نیز پردازش می‌کند و دامنه پوشش handler را به خطاهای حافظه سطح پایین گسترش می‌دهد.

پیاده‌سازی Global Exception Handler در iOS

پیاده‌سازی handler سراسری در iOS نیاز به تنظیم تابع C از طریق NSSetUncaughtExceptionHandler دارد. handler در لحظه استثنای مدیریت‌نشده به صورت همزمان فراخوانی می‌شود و زمینه کامل خطا را دریافت می‌کند.

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

    // لاگ crash را در فایل محلی ذخیره می‌کنیم
    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: handler فقط استثناهای Objective-C را رهگیری می‌کند. خطاهای Swift که از مکانیزم throw-catch استفاده می‌کنند، به این handler نمی‌رسند — برای آنها پردازش جداگانه از طریق Swift Error Handling لازم است. از iOS 14 به بعد، اپل ترکیب NSSetUncaughtExceptionHandler با Signals API را برای حداکثر پوشش توصیه می‌کند.

طبق Apple Technical Note TN2151، پس از فراخوانی handler، برنامه باید در عرض 5 ثانیه خاتمه یابد. هرگونه تلاش برای ادامه اجرا پس از بازگشت از handler منجر به رفتار نامشخص و crash مجدد می‌شود.

پیاده‌سازی Global Exception Handler در Android

Android مکانیزم انعطاف‌پذیرتری برای مدیریت سراسری استثناها از طریق Thread.setDefaultUncaughtExceptionHandler ارائه می‌دهد. handler ارجاع به نخی که استثنا در آن رخ داده و خود شیء Throwable را دریافت می‌کند.

kotlin
class GlobalExceptionHandler : Thread.UncaughtExceptionHandler {

    override fun uncaughtException(thread: Thread, throwable: Throwable) {
        // لاگ crash را در فایل ذخیره می‌کنیم
        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: هر نخ handler مخصوص خود را دارد و setDefaultUncaughtExceptionHandler handler را برای تمام نخ‌هایی که handler فردی ندارند تنظیم می‌کند. این پوشش سراسری را تضمین می‌کند — از نخ UI گرفته تا AsyncTask و کوروتین‌های پس‌زمینه.

در Android 12+ محدودیتی ایجاد شد: پس از فراخوانی uncaughtException، برنامه باید در عرض 100 میلی‌ثانیه خاتمه یابد. اگر handler عملیات طولانی انجام دهد، سیستم ممکن است فرآیند را قبل از اتمام نوشتن لاگ بکشد. توصیه می‌شود از سرویس پس‌زمینه برای ارسال گزارش crash استفاده شود.

بهترین شیوه‌ها در کار با Global Exception Handler

اولین قانون — سعی در بازیابی برنامه پس از استثنای مدیریت‌نشده نکنید. وضعیت برنامه پس از crash نامشخص است و ادامه کار می‌تواند منجر به آسیب دیدن داده‌های کاربر شود.

به حداقل رساندن زمان کار handler

محدودیت زمانی — اصلی‌ترین محدودیت فنی Global Exception Handler است. در iOS این 5 ثانیه، در Android — 100 میلی‌ثانیه است. در داخل handler فقط ذخیره حداقل مجموعه داده مجاز است: نوع استثنا، پشته فراخوانی و وضعیت چند متغیر کلیدی.

ارسال درخواست‌های شبکه، نوشتن در پایگاه داده و سریال‌سازی پیچیده باید به مکانیزم به تأخیر افتاده منتقل شود — مثلاً ذخیره لاگ در فایل و ارسال آن در راه‌اندازی بعدی برنامه.

ترکیب با سیستم‌های گزارش crash

سرویس‌های گزارش crash — Firebase Crashlytics، Sentry، Bugsnag — خودشان handler سراسری را تنظیم می‌کنند. اگر توسعه‌دهنده handler خود را روی آنها تنظیم کند، باید پس از اقدامات خود کنترل را به سیستم گزارش crash واگذار کند. در Android از ترکیب handlerها استفاده می‌شود: اجرای منطق خود، سپس فراخوانی handler قبلی.

برای Firebase Crashlytics توصیه می‌شود که اصلاً Thread.setDefaultUncaughtExceptionHandler سفارشی تنظیم نکنید — Crashlytics SDK این کار را به صورت خودکار در هنگام مقداردهی اولیه انجام می‌دهد.

ثبت لاگ اطلاعات اضافی

زمینه کاربر — علاوه بر پشته فراخوانی استاندارد، ثبت نسخه برنامه، نسخه سیستم عامل، مقدار حافظه آزاد و زمان کار تا crash مفید است. این داده‌ها برای بازتولید و رفع مشکل حیاتی هستند.

در iOS می‌توان از NSSetUncaughtExceptionHandler نه تنها برای نوشتن، بلکه برای ذخیره موقت داده‌ها در NSUserDefaults با پرچم synchronize استفاده کرد — این امر ذخیره‌سازی داده‌ها را حتی با خاتمه فوری فرآیند تضمین می‌کند.

تست handler قبل از انتشار

تست اجباری — Global Exception Handler باید در هر مرحله از CI/CD آزمایش شود. در iOS می‌توان استثنای آزمایشی را از طریق @throw NSException و در Android از طریق throw RuntimeException() ایجاد کرد. بررسی می‌شود که handler فراخوانی شده، لاگ ذخیره شده و برنامه به درستی خاتمه می‌یابد.

طبق Google I/O 2023، بیش از 30% از crashها در تولید روی دستگاه‌هایی رخ می‌دهد که توسعه‌دهنده آن‌ها را آزمایش نکرده است — نسخه‌های مختلف Android، سیستم‌عامل سفارشی، حافظه محدود.

اشتباهات رایج در استفاده از handler

اولین و رایج‌ترین اشتباه — تلاش برای ادامه اجرای برنامه پس از پردازش استثنا. پس از فراخوانی uncaughtException، برنامه در وضعیت ناپایداری قرار دارد و هرگونه عملیات بعدی می‌تواند باعث آبشاری از خطاها و آسیب دیدن داده‌ها شود.

دومین اشتباه — انجام عملیات طولانی در داخل handler. درخواست‌های شبکه، نوشتن فایل‌های بزرگ یا محاسبات پیچیده قبل از خاتمه اجباری فرآیند زمان اتمام ندارند. طبق Apple Technical Q&A QA1468، تلاش برای ارسال درخواست HTTP در داخل handler علت اصلی از دست رفتن گزارش‌های crash است.

سومین اشتباه — نادیده گرفتن نخ‌های پس‌زمینه. Global Exception Handler که فقط برای نخ اصلی تنظیم شده، از crash در کوروتین‌ها، DispatchQueue، AsyncTask یا RxJava محافظت نمی‌کند. در Android هر نخ باید handler مخصوص خود را داشته باشد — و setDefaultUncaughtExceptionHandler این مشکل را فقط برای نخ‌های بدون handler فردی حل می‌کند.

چهارمین اشتباه — عدم وجود fallback برای سیگنال‌های سیستم عامل. NSSetUncaughtExceptionHandler در iOS SIGABRT، SIGSEGV و SIGBUS را رهگیری نمی‌کند. برای این سیگنال‌ها نیاز به تنظیم جداگانه handlerها از طریق sigaction API است. توسعه‌دهندگان تنها زمانی متوجه این می‌شوند که برنامه بدون هیچ گزارش crashی از کار می‌افتد.

پنجمین اشتباه — ثبت لاگ داده‌های محرمانه. ایمیل‌ها، توکن‌های احراز هویت یا داده‌های شخصی کاربران وارد لاگ crash می‌شوند. این GDPR و Apple App Store Review Guidelines را نقض می‌کند. همیشه داده‌های ارسالی را از طریق regex یا whitelist فیلدهای مجاز فیلتر کنید.

سوالات متداول

آیا می‌توان برنامه را پس از استثنای سراسری بازیابی کرد؟

خیر — پس از فراخوانی Global Exception Handler وضعیت برنامه نامشخص است. هرگونه تلاش برای ادامه کار می‌تواند منجر به آسیب دیدن داده‌ها شود. تنها اقدام صحیح — ذخیره لاگ crash و خاتمه فرآیند است.

آیا Global Exception Handler همه انواع خطاها را رهگیری می‌کند؟

همه را نه — در iOS، NSSetUncaughtExceptionHandler فقط استثناهای Objective-C را می‌گیرد. خطاهای Swift و سیگنال‌های سیستم عامل (SIGSEGV, SIGABRT) نیاز به handlerهای جداگانه دارند. در Android، Thread.setDefaultUncaughtExceptionHandler همه RuntimeExceptionها را رهگیری می‌کند اما خطاهای کد native از طریق JNI را رهگیری نمی‌کند.

چگونه کنترل را پس از handler خود به سیستم گزارش crash واگذار کنیم؟

ارجاع به handler قبلی را از طریق Thread.getDefaultUncaughtExceptionHandler() قبل از تنظیم handler خود ذخیره کنید. در پایان handler خود previousHandler.uncaughtException(thread, throwable) را فراخوانی کنید — این تضمین می‌کند که Crashlytics یا Sentry داده‌های خود را دریافت کنند.

اگر crash در کد native C/C++ رخ دهد چه کنیم؟

برای کد native، پردازش سیگنال‌ها از طریق sigaction() مورد نیاز است — SIGSEGV, SIGABRT, SIGBUS. در Android می‌توان از Google Breakpad یا Crashpad استفاده کرد. در iOS از نسخه 13 به بعد Signals API برای پردازش استثناهای mach در دسترس است.

آیا Global Exception Handler می‌تواند بر عملکرد تأثیر بگذارد؟

خیر — تنظیم handler فقط در لحظه وقوع استثنا تأثیر دارد. در کار عادی برنامه هیچ سرباری وجود ندارد. تنها خطر — نشت حافظه اگر handler ارجاع به Activity یا Context را نگه دارد و از جمع‌آوری زباله آن‌ها جلوگیری کند.

خلاصه

  • Global Exception Handler — آخرین خط دفاعی قبل از crash، اجباری در هر برنامه تولیدی
  • iOS NSSetUncaughtExceptionHandler استثناهای Objective-C را با محدودیت ۵ ثانیه برای پردازش رهگیری می‌کند
  • Android Thread.setDefaultUncaughtExceptionHandler برای تمام نخ‌های بدون handler فردی کار می‌کند
  • زمان کار handler باید حداقل باشد — ذخیره داده و خاتمه فرآیند بدون تلاش برای بازیابی
  • سیگنال‌های سیستم عامل (SIGSEGV, SIGABRT) توسط handlerهای استاندارد رهگیری نمی‌شوند — نیاز به sigaction API است
  • سیستم‌های گزارش crash باید از طریق ترکیب handlerها فراخوانی شوند و کنترل پس از منطق خود واگذار شود
  • تست handler در CI/CD — مرحله اجباری که از از دست رفتن گزارش‌های crash در تولید جلوگیری می‌کند

ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد

IT Sectr از سال 2017 برنامه‌های iOS و Android را برای استارتاپ‌ها و کسب‌وکارها ایجاد می‌کند. ما به شما مشاوره می‌دهیم و بهترین راه‌حل را پیشنهاد خواهیم کرد.

بحث درباره پروژه

همچنین بخوانید