StrictMode: ما هو، وضع القواعد الصارمة وتصحيح الأخطاء في Android

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

StrictMode هي أداة مطور مدمجة في Android SDK تكتشف وتُبلغ عن عمليات الإدخال/الإخراج العرضية واستدعاءات الشبكة على الخيط الرئيسي للتطبيق في الوقت الفعلي. لا تُصلح الأخطاء بل تعمل ككاشف — تُلقي استثناءات أو تكتب في LogCat عند انتهاك السياسات المُكونة. وفقًا لـ Google, 2024، يمكن للتكوين الصحيح لـ StrictMode اكتشاف ما يصل إلى 80% من مشكلات الأداء قبل إصدار التطبيق.

النقاط الرئيسية

  • StrictMode — كاشف انتهاكات الأداء على الخيط الرئيسي في Android
  • سياسات القرص (disk_read, disk_write) والشبكة (network) تُشكل المجموعة الأساسية من الفحوصات
  • الأداة لا تُصلح المشكلات بل تُبلغ عنها عبر LogCat أو حوار أو تعطل
  • التكوين يتم في Application.onCreate باستخدام setThreadPolicy + setVmPolicy
  • أنماط العقوبة: رمي استثناء (death)، تسجيل، إشعار في dropbox

ما هو StrictMode

StrictMode هي واجهة برمجة تطبيقات (API) مضمنة في Android SDK منذ API Level 9 (Android 2.3 Gingerbread). مهمتها اكتشاف التنفيذ العرضي للعمليات الثقيلة على الخيط الرئيسي (UI) التي قد تمنع عرض الواجهة في وقت التشغيل. الخيط الرئيسي مسؤول عن معالجة إدخال المستخدم وحساب التخطيط والعرض — أي حظر أطول من 16 مللي ثانية يؤدي إلى فقدان إطار.

فلسفة الأداة

StrictMode تتبع مبدأ «فشل سريعاً» — اكتشاف المشكلة في أقرب وقت ممكن، ويفضل أن يكون ذلك في لحظة ظهورها الأول. بدلاً من انتظار شكاوى المستخدمين من البطء، يتلقى المطور إشارة (سجلات أو حوار أو تعطل) مباشرة في مرحلة التطوير. لا تتطلب الأداة مكتبات إضافية أو تكوين Gradle —فقط بضعة أسطر من الكود في Application.onCreate، وتعمل تلقائيًا على جميع الأجهزة.

الجمهور المستهدف

StrictMode مصمم لجميع مطوري Android، بغض النظر عن الخبرة. للمبتدئين، يساعد في تكوين عادات جيدة (عدم إجراء طلبات الشبكة في خيط UI)؛ للمطورين ذوي الخبرة، يؤتمتة مراقبة الجودة في خط أنابيب CI/CD. المشاريع الكبيرة (Google, Uber, Spotify) تُدرج StrictMode في إصدارات التصحيح مع penaltyDeath، وتُعطله في إصدارات الإصدار عبر التحقق من BuildConfig.DEBUG.

كيف يعمل StrictMode

StrictMode تعترض استدعاءات النظام التي قد تمنع الخيط وتقارنها بمجموعة السياسات النشطة. إذا تطابق الاستدعاء مع سياسة وتم تنفيذه على الخيط الرئيسي، تطبق StrictMode العقوبة المحددة. آلية الاعتراض مُطبقة عبر خطاف داخل العملية — لا تستخدم الانعكاس (reflection) وتعمل بأقل حمل إضافي.

آلية الكشف

عند تفعيل سياسة StrictMode، تحقن معالجها في نقاط دخول استدعاءات النظام (FileInputStream, FileOutputStream, Socket, URLConnection). عندما يستدعي التطبيق، على سبيل المثال، URLConnection.openStream على الخيط الرئيسي، تتحقق StrictMode من الخيط الحالي — إذا كان الخيط الرئيسي، تتفعل الأداة. في Android 6.0+، تم تعزيز الآلية: استدعاءات الشبكة على الخيط الرئيسي تولد NetworkOnMainThreadException حتى بدون StrictMode، لكن StrictMode تسمح أيضًا بالتحكم في إدخال/إخراج القرص.

العقوبة

يمكن أن يكون لكل سياسة نوع عقوبة خاص بها أو مجموعة: penaltyLog — يكتب في LogCat مع تتبع المكدس، penaltyDialog — يعرض حوارًا للمستخدم (التصحيح فقط)، penaltyDeath — يلقي استثناءً ويعطل التطبيق، penaltyDropBox — يحفظ البيانات في DropBoxManager للتحليل لاحقًا. لخطوط أنابيب CI/CD، يُوصى باستخدام penaltyDeath — يضمن عدم مرور أي دمج مع انتهاكات دون ملاحظة.

kotlin
class App : Application() {
    override fun onCreate() {
        super.onCreate()
        if (BuildConfig.DEBUG) {
            StrictMode.setThreadPolicy(
                StrictMode.ThreadPolicy.Builder()
                    .detectDiskReads()
                    .detectDiskWrites()
                    .detectNetwork()
                    .penaltyLog()
                    .penaltyDeath()
                    .build()
            )
        }
    }
}

سياسات StrictMode

StrictMode تقسم السياسات إلى مستويين: ThreadPolicy (مستوى الخيط — ما لا يمكن فعله على الخيط الرئيسي) و VmPolicy (الآلة الافتراضية — تسرب الذاكرة والموارد). يتم تكوين كلا المستويين بشكل مستقل ويعملان بالتوازي.

ThreadPolicy: القرص والشبكة

على مستوى الخيط، تتحكم StrictMode في أربعة أنواع من الانتهاكات: قراءة القرص (detectDiskReads)، كتابة القرص (detectDiskWrites)، عمليات الشبكة (detectNetwork)، والاستدعاءات البطيئة المخصصة (detectCustomSlowCalls). disk_read يتفعل عند أي قراءة لـ SharedPreferences أو SQLite أو ملفات على الخيط الرئيسي. network يتفعل عند طلبات HTTP و WebSocket واتصالات Socket. في Android 11+، تمت إضافة detectUnbufferedIO لكشف الإدخال/الإخراج غير المُخزن.

VmPolicy: تسرب الذاكرة

VmPolicy تتحكم في التسربات على مستوى الآلة الافتراضية ART: detectActivityLeaks (الأنشطة التي لم يتم تدميرها)، detectLeakedClosableObjects (Cursor و Stream و Socket غير مغلقة)، detectLeakedRegistrationObjects (BroadcastReceiver و ServiceConnection غير مسجلة). إذا اكتشفت VmPolicy أن Activity تم إنشاؤها ولكن لم يتم تدميرها بعد استدعاء onDestroy، فإنها تُخرج تتبع مكدس كامل — مما يوفر ساعات من تصحيح أخطاء تسرب الذاكرة.

السياسةالمستوىما تكتشفه
detectDiskReadsThreadقراءة SharedPrefs و SQLite وملفات في خيط UI
detectDiskWritesThreadالكتابة إلى SharedPrefs و SQLite وملفات في خيط UI
detectNetworkThreadأي عمليات شبكة في خيط UI
detectActivityLeaksVMالأنشطة التي تنجو من onDestroy
detectLeakedClosableObjectsVMCursor و Stream و Socket غير مغلقة

الاستدعاءات البطيئة المخصصة

من خلال detectCustomSlowCalls، يمكنك وضع علامة على طرقك الخاصة على أنها «مشبوهة» وتلقي تحذير عند تجاوز حد معين. على سبيل المثال، إذا كانت طريقتك loadUserProfile() تستغرق عادةً 5 مللي ثانية ولكنها تستغرق أحيانًا 200 مللي ثانية — لفها في StrictMode.noteSlowCall(«loadUserProfile»). إذا تجاوزت المدة الحد (افتراضيًا 2000 مللي ثانية)، ستقوم StrictMode بتوليد عقوبة. يتم تكوين الحد عبر setSlowCallDurationThreshold.

كيفية تكوين StrictMode

التكوين الأساسي لـ StrictMode يستغرق 10 أسطر من الكود ويتم في طريقة onCreate لفئة Application مخصصة. القاعدة الرئيسية: StrictMode تُفعل فقط في إصدارات التصحيح — في إصدارات الإصدار تُبطئ التطبيق ويمكن أن تُنشئ نتائج إيجابية خاطئة.

التكوين الأساسي

أنشئ فئة تمدد Application، وسجلها في AndroidManifest.xml عبر السمة android:name، وأضف تكوين StrictMode. ThreadPolicy.Builder يتضمن جميع الكاشفات وجميع أنواع العقوبات (باستثناء dialog — يعمل فقط عند توصيل مصحح أخطاء). VmPolicy.Builder يُضيف كاشفات لتسرب Activity وكائنات Closable. للمشاريع الكبيرة (100+ شاشة)، يُوصى بتكوين VmPolicy مع penaltyDeath على كاشف تسرب Activity — إنه صارم لكنه فعال.

kotlin
class App : Application() {
    override fun onCreate() {
        super.onCreate()
        if (BuildConfig.DEBUG) {
            StrictMode.setVmPolicy(
                StrictMode.VmPolicy.Builder()
                    .detectActivityLeaks()
                    .detectLeakedClosableObjects()
                    .detectLeakedRegistrationObjects()
                    .penaltyLog()
                    .penaltyDeath()
                    .build()
            )
        }
    }
}

التكامل مع CI/CD

للتحكم التلقائي في CI/CD، استخدم penaltyDeath — إذا انتهك أي اختبار السياسة، سيتعطل التطبيق مع استثناء. ادمجه مع Android Test Orchestrator لضمان تشغيل كل اختبار في عملية نظيفة. لاختبارات UI (Espresso, Compose Test)، اكتب TestRule مخصصًا يعترض انتهاكات StrictMode ويحولها إلى إخفاقات تأكيد. مثال: في @Before، فعّل StrictMode، وفي @After، تحقق من عدم وجود انتهاكات.

تكوين الحدود

افتراضيًا، الحد لـ customSlowCall هو 2000 مللي ثانية، لـ disk_read و disk_write — بدون حد (أي عملية تفعل). من خلال setSlowCallDurationThreshold و setSlowIoDurationThreshold، يمكنك تعيين القيم الخاصة بك بالمللي ثانية. إذا كان تطبيقك يقرأ SharedPreferences بشكل شرعي على الخيط الرئيسي (إعدادات صغيرة)، قم بزيادة الحد إلى 10–20 مللي ثانية — سيؤدي ذلك إلى تصفية القراءات السريعة مع الاحتفاظ بالبطيئة.

أفضل ممارسات StrictMode

StrictMode أداة قوية لكنها صعبة المراس. التكوين غير الصحيح يؤدي إلى ملايين النتائج الإيجابية الخاطئة، مما يتسبب في توقف المطورين عن الاهتمام بها. فيما يلي ممارسات مثبتة تم جمعها من تجربة فرق Android الكبيرة.

التفعيل فقط في إصدارات التصحيح

هذه قاعدة صارمة: StrictMode يجب أبدًا أن تكون نشطة في إصدارات الإصدار. استخدم علامة BuildConfig.DEBUG أو buildConfigField مخصص. في إصدارات الإصدار، تقوم العديد من المكتبات الخارجية بشكل شرعي بعمليات على الخيط الرئيسي (تهيئة SDK، كتابة ذاكرة التخزين المؤقت)، وستنشئ StrictMode نتائج إيجابية خاطئة. علاوة على ذلك، penaltyDialog في إصدار الإصدار سيعرض حوارًا للمستخدم النهائي — وهو أمر غير مقبول.

استخدم ثلاثة مستويات من الصرامة

للمشاريع الصغيرة (1–10 شاشات)، قم بتكوين penaltyLog — السجلات كافية للتحليل اليدوي. للمشاريع المتوسطة (10–50 شاشة)، أضف penaltyDeath على network و customSlowCalls. للمشاريع الكبيرة (50+ شاشة)، فعّل المجموعة الكاملة من السياسات مع penaltyDeath في CI/CD، و penaltyLog للتطوير المحلي. هذا التدرج يمنع إرهاق المطورين بالأعطال الخاطئة مع التحكم الصارم في الجودة في خط الأنابيب.

معالجة النتائج الإيجابية الخاطئة

بعض المكتبات (Firebase, Crashlytics, Adjust) تقوم بشكل شرعي بعمليات في الخلفية قد تكتشفها StrictMode بشكل غير صحيح. الحلول: أضف المكتبة إلى قائمة بيضاء عبر penaltyListener، أو حدّث المكتبة إلى إصدار مع تبديل صريح إلى خيط الخلفية، أو استخدم StrictMode.vmPolicy. في Android 11+، تم تقديم StrictMode.OnVmViolationListener للتصفية البرمجية للانتهاكات حسب تتبع المكدس.

kotlin
// تصفية النتائج الإيجابية الخاطئة عبر penaltyListener
StrictMode.setThreadPolicy(
    StrictMode.ThreadPolicy.Builder()
        .detectAll()
        .penaltyListener { violation ->
            val stack = violation.stackTraceToString()
            if ("com.google.firebase" !in stack) {
                logViolation(violation)
            }
        }
        .build()
)

StrictMode مقابل Android Lint مقابل المُحللات

StrictMode ليست أداة مراقبة الجودة الوحيدة في نظام Android البيئي. لفهم مكانتها، دعنا نقارنها مع Android Lint و Android Profiler و Perfetto عبر معايير رئيسية: وقت الفحص وعمق التحليل والأتمتة.

المعيارStrictModeAndroid LintProfiler / Perfetto
وقت الفحصوقت التشغيل (أثناء تشغيل التطبيق)وقت الترجمة (قبل الإطلاق)وقت التشغيل (بعد الوفاة)
ما يفحصهالقرص، الشبكة، التسرباتXML، الكود، المواردCPU، الذاكرة، الشبكة، الطاقة
الأتمتةCI/CD عبر penaltyDeathمهمة Gradle + lint-baselineيتطلب تحليلاً يدويًا
العمقخيط UI فقط والتسرباتتحليل ثابت للكودصورة كاملة للأداء
النتائج الإيجابية الخاطئةمتوسطة (تعتمد على المكتبات)منخفضة (قواعد مكونة)لا شيء (قياسات فعلية)

أفضل استراتيجية هي الجمع بين الطرق الثلاث: Android Lint يكتشف الأخطاء الواضحة في وقت الترجمة (مثل IdleHandler المنسي)، StrictMode تكتشف المشكلات في وقت التشغيل، و Android Profiler / Perfetto يُستخدم للتحليل العميق عندما لا تعطي الأداتان الأوليان إجابات. في المشاريع الحقيقية (Google Maps, Instagram)، يتم تقديم StrictMode في الأسبوع الثاني من التطوير — مباشرة بعد إعداد البنية الأساسية.

أمثلة كود مع StrictMode

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

الكشف عن SharedPreferences البطيء

عند بدء تشغيل التطبيق، سيكتشف StrictMode مع سياسة detectDiskReads قراءة SharedPreferences على الخيط الرئيسي. الحل: تحميل التكوين بشكل غير متزامن عبر CoroutineScope أو تخزينه مؤقتًا في الذاكرة عند بدء التشغيل. SharedPreferences تقرأ بشكل متزامن ملف XML من القرص — حتى مع ملف صغير (1–2 كيلوبايت)، تستغرق العملية من 1 إلى 5 مللي ثانية، وحتى 20 مللي ثانية على الأجهزة الرخيصة، مما قد يؤدي إلى فقدان الإطارات.

kotlin
// ❌ كود إشكالي — قراءة SharedPrefs في خيط UI
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // StrictMode detectDiskReads → انتهاك!
        prefs = getSharedPreferences("config", MODE_PRIVATE)
    }
}

// ✅ كود مصحح — قراءة عبر Coroutine
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        loadConfigAsync()
    }
}

اكتشاف تسرب Activity

StrictMode مع VmPolicy.detectActivityLeaks ستكتشف Activity خرجت من المكدس (تم استدعاء finish) لكن كائن Activity يظل في الذاكرة بسبب مرجع ثابت أو استدعاء غير مسجل. السيناريو النموذجي: تسجيل EventBus أو LocationListener في onResume دون استدعاء unregister في onPause. VmPolicy ستُخرج تتبع مكدس يشير إلى السطر الذي تم فيه إنشاء المرجع.

kotlin
// ❌ تسرب — استدعاء غير ملغى
private var locationCallback: LocationCallback? = null

override fun onResume() {
    super.onResume()
    locationCallback = LocationCallback(this::onLocationUpdated)
    locationManager.register(locationCallback) // StrictMode → تسرب!
}

override fun onPause() {
    super.onPause()
    // نسيت: locationManager.unregister(locationCallback)
}

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

هل يُبطئ StrictMode التطبيق؟

StrictMode تُضيف بالفعل حملًا إضافيًا صغيرًا — يتم فحص كل استدعاء نظام مقابل السياسات. تأثير الأداء هو 1–3% في إصدارات التصحيح وغائب في إصدارات الإصدار (حيث يتم تعطيل StrictMode). عند تفعيل detectAll على الأجهزة القديمة (Android 6–8)، قد يصل الحمل الإضافي إلى 5%، لذلك يُوصى بتكوين السياسات الضرورية فقط.

هل يمكن استخدام StrictMode مع Jetpack Compose؟

نعم، StrictMode متوافقة تمامًا مع Jetpack Compose. سياسات القرص والشبكة تعمل على مستوى الإطار، بغض النظر عن إطار UI. علاوة على ذلك، في Compose تكون خطورة حظر UI أعلى — يعيد Compose رسم الإطارات بمعدل 120 إطارًا في الثانية على الأجهزة عالية معدل التحديث، لذلك تصبح 5 مللي ثانية إضافية في قراءة الملف أكثر وضوحًا.

لماذا لا تتفعل StrictMode عند قراءة SharedPreferences؟

بدءًا من Android 8.1 (API 27)، قد تستخدم SharedPreferences التخزين المؤقت في الذاكرة — إذا تمت قراءة الملف بالفعل، فلن تتفعل إعادة القراءة StrictMode. تأكد من استدعاء getSharedPreferences لأول مرة (قراءة باردة) وأن سياسة detectDiskReads نشطة. تحقق أيضًا من عدم تجاوز StrictMode في جزء بدون أب.

كيفية تعطيل StrictMode للاختبارات الفردية؟

في اختبارات JUnit، استخدم StrictMode.allowThreadDiskReads() و StrictMode.allowThreadDiskWrites() في @Before، واستعد الإعدادات في @After عبر StrictMode.enableDefaults(). لاختبارات Instrumentation، استخدم TestRunner مخصصًا مع حفظ مؤقت للسياسة الأصلية. في اختبارات Espresso، من المناسب لف الكود الحساس لـ StrictMode في IdlingResource.

هل StrictMode ضروري في Kotlin Multiplatform؟

StrictMode تعمل فقط على منصة Android عبر Android SDK. في Kotlin Multiplatform (KMP)، لا يمكن لكود commonMain استخدام StrictMode، ولكن بالنسبة لـ androidMain يمكنك إضافتها كالمعتاد. بالنسبة لجزء iOS، استخدم نظيرًا — تأكيد DispatchQueue.main.async للخيط الرئيسي.

الملخص

  • StrictMode — كاشف في وقت التشغيل لمشكلات الأداء على الخيط الرئيسي في Android
  • تنقسم السياسات إلى ThreadPolicy (القرص، الشبكة) و VmPolicy (تسرب الذاكرة)
  • التكوين يستغرق 10 أسطر من الكود في Application.onCreate مع التحقق من BuildConfig.DEBUG
  • لـ CI/CD، استخدم penaltyDeath — انتهاك السياسة يؤدي إلى تعطل التطبيق
  • StrictMode لا تستبدل بل تُكمل Android Lint و Perfetto
  • التصفية المناسبة للنتائج الإيجابية الخاطئة هي مفتاح الاستخدام الفعال للأداة
  • يُوصى بإدخال StrictMode في الأسبوع الثاني من تطوير المشروع

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

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

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

اقرأ أيضًا