Non-Fatal Error — هو خطأ لا يؤدي إلى إنهاء عمل التطبيق ويسمح بمواصلة تنفيذ البرنامج. على عكس الخطأ المميت، يمكن التقاط الأخطاء غير المميتة ومعالجتها وتسجيلها دون فقدان جلسة المستخدم. وفقًا لوثائق Firebase Crashlytics، 2024، حوالي 70% من جميع الأخطاء المسجلة في تطبيقات الإنتاج هي غير مميتة، لكن تجاهلها يؤدي إلى تراكم الديون الفني وتدهور تدريجي لتجربة المستخدم. المعالجة الصحيحة للأخطاء غير المميتة هي واحدة من المهارات الأساسية لمطور التطبيقات.
الخلاصة
Non-Fatal Error — هو استثناء أو حالة خطأ لا تسبب إنهاء العملية. يستمر التطبيق في العمل، ولكنه قد يكون في حالة غير صحيحة: لم يتم تحميل البيانات، لم يتم إرسال الطلب، لم يتم عرض عنصر الواجهة. المستخدم إما لا يلاحظ الخطأ، أو يرى رسالة ويواصل استخدام التطبيق.
الخطأ غير المميت يترك دائمًا للبرنامج مسارًا للتعافي. يمكن لمعالج الأخطاء تقديم بيانات بديلة، إعادة المحاولة، أو عرض عنصر نائب في الواجهة. الهدف الرئيسي هو منع التعطل والحفاظ على تجربة مستخدم مقبولة. يجب على المطور أن يخطط صراحةً لسيناريو التعافي في كل كتلة catch.
وفقًا لـ Instabug 2024، 65% من المستخدمين يحذفون التطبيق بعد تفاعلين فاشلين. الأخطاء غير المميتة التي تُترك دون معالجة تتراكم وتقلل الجودة العامة. التسجيل المنهجي وإصلاح الأخطاء غير المميتة هو طريق مباشر لتحسين الاحتفاظ بالمستخدمين ورفع التقييمات في متاجر التطبيقات.
أخطاء الشبكة هي النوع الأكثر شيوعًا من الأخطاء غير المميتة في تطبيقات الجوال. انتهاء مهلة الاتصال، فقدان الشبكة، رمز حالة خادم غير صحيح — كل هذه الحالات يتم التقاطها ومعالجتها دون تعطل. تظهر للمستخدم رسالة عدم توفر الخدمة مع خيار إعادة المحاولة. نمط إعادة المحاولة مع التأخير الأسي هو النمط النموذجي لأخطاء الشبكة.
تنسيق استجابة خادم غير صحيح، حقل إجباري مفقود، نوع بيانات غير صالح — أخطاء التحليل غير مميتة إذا كان التطبيق يعالج البيانات غير الصحيحة بشكل صحيح. النهج النموذجي هو استخدام قيم افتراضية احتياطية وتسجيل خطأ التحليل مع سياق الطلب لتحليل لاحق على الخادم.
مشاكل تحميل الصور، خطوط غير صحيحة، أخطاء في التخطيط — كلها غير مميتة ولكنها تقلل من تجربة المستخدم. صور العناصر النائبة والقيم الاحتياطية تساعد في تجنب الشاشات الفارغة وتجعل الأخطاء أقل وضوحًا. في React Native، يتم استخدام Error Boundary لأخطاء واجهة المستخدم مع عرض مكون بديل.
أخطاء في الحسابات، عدم تطابق الحالة، انتقالات غير صحيحة بين الشاشات — الأخطاء المنطقية غالبًا لا تسبب تعطلاً ولكنها تؤدي إلى سلوك غير صحيح للتطبيق. يصعب اكتشافها دون تسجيل ومراقبة منهجيين لأنها لا تنشئ تقرير تعطل وتبقى غير ملحوظة حتى شكوى المستخدم.
Non-Fatal Error يختلف عن الخطأ المميت في أنه يترك للبرنامج فرصة لمواصلة العمل. الخطأ المميت هو حالة لا يمكن للتطبيق التعافي منها: إلغاء مرجع مؤشر فارغ، تجاوز سعة المكدس، نفاد الذاكرة. يمكن التقاط الخطأ غير المميت ومعالجته ومواصلة التنفيذ، بينما يتطلب الخطأ المميت إعادة تشغيل التطبيق.
| الخاصية | Non-Fatal Error | Fatal Error |
|---|---|---|
| إنهاء التطبيق | لا | نعم |
| إمكانية التعافي | نعم، عبر catch | لا |
| التسجيل | من الكود عبر recordException | فقط بواسطة مبلغ التعطل |
| تأثير على تجربة المستخدم | إزعاج مؤقت | فشل كامل للجلسة |
| مثال | مهلة شبكة، خطأ تحليل | NullPointerException, OOM |
الحدود بين non-fatal و fatal قد تعتمد على التنفيذ. مهلة الشبكة في تطبيق تُعالج كغير مميتة (إعادة محاولة بعد 1–2 ثانية)، بينما في آخر قد تكون مميتة (تعطل في حالة عدم وجود معالج). معالجة الأخطاء عالية الجودة تحول المواقف التي قد تكون مميتة إلى غير مميتة، مما يزيد من استقرار التطبيق. تصميم نظام معالجة الأخطاء هو أحد المهام المعمارية الرئيسية عند تطوير تطبيق جوال بمتطلبات موثوقية عالية. نظام مراقبة مدمج يسمح للفريق باكتشاف وإصلاح الأخطاء غير المميتة بسرعة قبل أن تؤثر على عدد كبير من المستخدمين.
Firebase Crashlytics هو الأداة الرئيسية لتسجيل الأخطاء غير المميتة في تطبيقات الجوال. تتيح طريقة recordException التقاط استثناء غير مميت مع تتبع كامل للمكدس وسياق التنفيذ دون مقاطعة التطبيق. على عكس تقارير التعطل، يمكن استدعاء recordException في أي مكان في الكود لتسجيل الاستثناءات التي تم التقاطها.
fun fetchUserData(userId: String) {
try {
val response = apiService.getUser(userId)
updateUI(response)
} catch (e: IOException) {
Crashlytics.log("Network error for user $userId")
Crashlytics.recordException(e)
showRetryDialog()
} catch (e: JsonParseException) {
// Non-fatal: استخدام البيانات الاحتياطية
Crashlytics.recordException(e)
showFallbackContent()
}
}
// التسجيل بمفاتيح مخصصة
Crashlytics.setCustomKey("screen", "Profile")
Crashlytics.setCustomKey("api_version", "v3")
Sentry هو بديل لـ Crashlytics مع تشخيص أكثر تفصيلاً للأخطاء غير المميتة. يوفر Sentry SDK طريقة captureException التي ترسل تفاصيل الاستثناء إلى الخادم. الميزة الرئيسية لـ Sentry هي تجميع الأخطاء غير المميتة المتشابهة في مشكلة واحدة، وتحليل تكرار الحدوث، وتوفير سياق التنفيذ في شكل breadcrumbs — تسلسل إجراءات المستخدم قبل الخطأ.
ليست كل الأخطاء غير المميتة تحتاج إلى تسجيل. الحالات المتوقعة — فشل الشبكة عند عدم وجود اتصال — يمكن تسجيلها بشكل انتقائي. الأخطاء غير المتوقعة — NullPointerException في كود معالج، تنسيق بيانات غير صالح، أخطاء منطقية — يجب تسجيلها دائمًا. كل فريق يحدد عتبة الأهمية الخاصة به: في المتوسط، 10 إلى 20 خطأ غير مميت فريد لكل 1000 مستخدم يوميًا يعتبر طبيعيًا. من المهم إعداد تنبيهات للزيادة الحادة في الأخطاء غير المميتة — قد يشير ذلك إلى مشاكل في إصدار API جديد أو تراجع بعد الإصدار.
الآلية الأساسية للمعالجة هي try-catch، التي تلتقط الاستثناء وتنفذ كود التعافي. لعمليات الشبكة، النمط النموذجي هو إعادة المحاولة مع التأخير الأسي. لأخطاء التحليل، النهج هو استخدام قيم افتراضية احتياطية وتسجيل السياق لتحليل لاحق في الخادم.
func loadImage(from url: URL) -> UIImage? {
do {
let data = try Data(contentsOf: url)
return UIImage(data: data)
} catch {
Logger.shared.logError(error: "Image load failed: \(url)")
return UIImage(named: "placeholder")
}
}
func performRequest() async throws -> Data {
var lastError: Error? = nil
for attempt in 0..<3 {
do {
return try await URLSession.shared.data(from: url)
} catch {
lastError = error
try await Task.sleep(UInt64(pow(2, attempt)) * 1_000_000_000)
}
}
throw lastError ?? URLError(.unknown)
}
أنواع Result — نهج بديل بدون استثناءات. ترجع الدالة فئة Result مختومة مع خيارات Success و Failure. الكود المستديع يعالج كلا الخيارين بشكل صريح، مما يلغي الأخطاء غير المعالجة. أنواع Result شائعة في Kotlin (Result
لكل نوع من الأخطاء غير المميتة، يجب التخطيط لاستراتيجية تعافي: تحميل البيانات المخزنة مؤقتًا عند خطأ الشبكة، استخدام القيم الافتراضية عند خطأ التحليل، إعادة تهيئة المكون عند خطأ واجهة المستخدم. الممارسة الجيدة هي عرض رسالة خطأ للمستخدم عبر toast أو snackbar دون حظر التفاعل مع التطبيق بالكامل. من المهم التمييز بين الأخطاء القابلة للاسترداد وغير القابلة للاسترداد — لهذه الأخيرة، ستكون استراتيجية التعافي مختلفة، مثل اقتراح إعادة تشغيل الشاشة أو مسح البيانات. التخزين المؤقت للحالة الناجحة السابقة هو غالبًا الطريقة الأبسط والأكثر فعالية لمعالجة الأخطاء غير المميتة على المنصات المحمولة.
الأسئلة الشائعة
التحذير هو تنبيه من المترجم أو المحلل الثابت حول مشكلة محتملة في الكود. الخطأ غير المميت هو استثناء وقت تشغيل حدث بالفعل لكنه لم يؤد إلى تعطل. يمكن إصلاح التحذير قبل التجميع؛ بينما يجب معالجة الخطأ غير المميت أثناء التنفيذ عبر كتلة catch.
لا، التسجيل المفرط يزعج المراقبة. يجب تسجيل الأخطاء غير المتوقعة في الإنتاج وتجاهل الحالات المتوقعة: فشل الشبكة عند عدم الاتصال يمكن تسجيله بشكل انتقائي، لكن NullPointerException في كود معالج يجب تسجيله دائمًا. كل فريق يحدد عتبة الأهمية حسب سياق التطبيق.
في SwiftUI، يُستخدم ObservableObject مع حقل @Published errorState لتتبع حالة الخطأ. تشترك view في التغييرات وتعرض محتوى بديل. قبل iOS 17، كان يُستخدم Combine مع معالجات؛ بدءًا من iOS 17، تُستخدم SwiftData ووحدات @Observable للتحديثات التفاعلية للواجهة.
نعم، إذا تسبب الخطأ في تفاعل تسلسلي. مثال: فشل غير مميت في تحميل صورة يمكن أن يؤدي إلى حالة واجهة غير صحيحة، والتي تسبب بعد ذلك تعطلًا عند محاولة العرض. المعالجة عالية الجودة للأخطاء غير المميتة في كل مستوى تمنع تصعيدها إلى المستوى المميت.
في iOS، تُعالج الأخطاء غير المميتة عبر do-catch مع throw؛ في Android، عبر try-catch مع الاستثناءات. يستخدم iOS NSError مع نطاقات ورموز خطأ؛ يستخدم Android استثناءات Java/Kotlin. يعمل Crashlytics بشكل متطابق على كلا المنصتين عبر recordException، مما يوفر واجهة مراقبة موحدة.
الملخص
سنقوم بتطوير تطبيق جوال جاهز
تقدم IT Sectr تطبيقات iOS وAndroid للشركات الناشئة والشركات منذ عام 2017. سوف نقدم لك النصح ونقترح أفضل حل.
اقرأ أيضًا