StrictMode: این چیست، حالت قوانین سختگیرانه و دیباگ در Android

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

StrictMode — این یک ابزار توسعه‌دهنده داخلی در Android SDK است که در زمان واقعی عملیات تصادفی ورودی/خروجی و تماس‌های شبکه در رشته اصلی برنامه را تشخیص داده و گزارش می‌دهد. این ابزار خطاها را برطرف نمی‌کند، بلکه به عنوان یک آشکارساز عمل می‌کند — هنگام نقض سیاست‌های تعیین‌شده استثنا پرتاب می‌کند یا در LogCat می‌نویسد. طبق داده‌های Google, 2024، پیکربندی صحیح StrictMode امکان تشخیص تا 80٪ مشکلات عملکرد را حتی قبل از انتشار برنامه فراهم می‌کند.

نکات اصلی

  • StrictMode — آشکارساز نقض عملکرد در رشته اصلی Android
  • سیاست‌های دیسک (disk_read, disk_write) و شبکه (network) مجموعه پایه بررسی‌ها را تشکیل می‌دهند
  • ابزار مشکلات را برطرف نمی‌کند، بلکه از طریق LogCat، دیالوگ یا crash اطلاع می‌دهد
  • پیکربندی در Application.onCreate انجام می‌شود و از setThreadPolicy + setVmPolicy استفاده می‌کند
  • حالت‌های Penalty: پرتاب استثنا (death)، ثبت در Log، اعلان در dropbox

StrictMode چیست

StrictMode — API ای است که از API Level 9 (Android 2.3 Gingerbread) در Android SDK گنجانده شده است. وظیفه آن شناسایی در زمان اجرای اجرای تصادفی عملیات سنگین در رشته اصلی (UI) است که می‌توانند رندر رابط را مسدود کنند. رشته اصلی مسئول پردازش ورودی کاربر، محاسبه چیدمان و رندر است — هر مسدودیتی بیش از 16 میلی‌ثانیه منجر به افت فریم می‌شود.

فلسفه ابزار

StrictMode از اصل fail fast پیروی می‌کند — مشکل را در اولین فرصت، ترجیحاً در لحظه اولین ظهور آن، شناسایی کند. به جای انتظار برای شکایت کاربران در مورد کندی، توسعه‌دهنده سیگنال (لاگ، دیالوگ یا crash) را مستقیماً در مرحله توسعه دریافت می‌کند. این ابزار به کتابخانه‌های اضافی و پیکربندی Gradle نیاز ندارد — فقط چند خط کد در Application.onCreate کافی است و به طور خودکار در همه دستگاه‌ها کار می‌کند.

مخاطب هدف

StrictMode برای همه توسعه‌دهندگان Android، صرف نظر از تجربه، طراحی شده است. به مبتدیان کمک می‌کند عادت‌های صحیح شکل دهند (درخواست شبکه در رشته UI انجام ندهند)، به باتجربه‌ها — کنترل کیفیت را در خط لوله CI/CD خودکار کنند. پروژه‌های بزرگ (Google, Uber, Spotify) StrictMode را با penaltyDeath در بیلدهای debug فعال می‌کنند و در بیلدهای release از طریق بررسی BuildConfig.DEBUG غیرفعال می‌کنند.

StrictMode چگونه کار می‌کند

StrictMode تماس‌های سیستمی را که می‌توانند رشته را مسدود کنند رهگیری می‌کند و آنها را با مجموعه سیاست‌های فعال مقایسه می‌کند. اگر تماس با سیاست مطابقت داشته باشد و در رشته اصلی اجرا شود، StrictMode penalty (جریمه) تعیین‌شده را اعمال می‌کند. مکانیسم رهگیری از طریق هوک درون‌فرآیندی پیاده‌سازی شده است — از reflection استفاده نمی‌کند و با حداقل overhead کار می‌کند.

مکانیسم تشخیص

هنگام فعال‌سازی سیاست، StrictMode کنترل‌کننده خود را در نقطه ورود تماس‌های سیستمی (FileInputStream, FileOutputStream, Socket, URLConnection) تزریق می‌کند. وقتی برنامه مثلاً URLConnection.openStream را در رشته اصلی فراخوانی می‌کند، StrictMode رشته فعلی را بررسی می‌کند — اگر main thread باشد، ابزار فعال می‌شود. در Android 6.0+ مکانیسم تقویت شده است: تماس‌های شبکه در main thread حتی بدون StrictMode NetworkOnMainThreadException ایجاد می‌کنند، اما StrictMode به شما امکان کنترل disk I/O را نیز می‌دهد.

Penalty (جریمه)

هر سیاست می‌تواند نوع penalty خود یا ترکیبی داشته باشد: penaltyLog — ثبت در LogCat با stack trace، penaltyDialog — نمایش دیالوگ به کاربر (فقط در debug)، penaltyDeath — پرتاب استثنا و crash برنامه، penaltyDropBox — ذخیره داده‌ها در DropBoxManager برای تحلیل بعدی. برای خط لوله CI/CD، penaltyDeath توصیه می‌شود — این تضمین می‌کند که هیچ merge با نقض بدون شناسایی عبور نمی‌کند.

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 (Activity که نابود نشده‌اند)، detectLeakedClosableObjects (Cursor, Stream, Socket بسته نشده)، detectLeakedRegistrationObjects (BroadcastReceiver, ServiceConnection لغو نشده). اگر VmPolicy تشخیص دهد که Activity ایجاد شده اما پس از فراخوانی onDestroy نابود نشده است، full stack trace را خروجی می‌دهد — این کار ساعت‌ها اشکال‌زدایی نشت حافظه را صرفه‌جویی می‌کند.

سیاستسطحچه چیزی را تشخیص می‌دهد
detectDiskReadsThreadخواندن SharedPrefs، SQLite، فایل‌ها در رشته UI
detectDiskWritesThreadنوشتن در SharedPrefs، SQLite، فایل‌ها در رشته UI
detectNetworkThreadهرگونه عملیات شبکه در رشته UI
detectActivityLeaksVMActivity که از onDestroy جان سالم به در برده‌اند
detectLeakedClosableObjectsVMCursor, Stream, Socket بسته نشده

برچسب‌های سفارشی (customSlowCall)

از طریق detectCustomSlowCalls می‌توانید متدهای خود را به عنوان مشکوک علامت‌گذاری کنید و هنگام تجاوز از آستانه تعیین‌شده هشدار دریافت کنید. به عنوان مثال، اگر متد loadUserProfile() شما معمولاً 5 میلی‌ثانیه اجرا می‌شود اما در برخی موارد 200 میلی‌ثانیه طول می‌کشد — آن را در StrictMode.noteSlowCall(loadUserProfile) بپیچید. اگر مدت زمان از آستانه (پیش‌فرض 2000 میلی‌ثانیه) تجاوز کند، StrictMode penalty تولید می‌کند. آستانه از طریق setSlowCallDurationThreshold قابل تنظیم است.

چگونه StrictMode را پیکربندی کنیم

پیکربندی پایه StrictMode 10 خط کد طول می‌کشد و در متد onCreate کلاس Application سفارشی انجام می‌شود. قانون اصلی: StrictMode فقط در بیلدهای debug فعال می‌شود — در بیلدهای release برنامه را کند می‌کند و می‌تواند هشدارهای کاذب ایجاد کند.

پیکربندی پایه

کلاسی ایجاد کنید که از Application ارث‌بری کند، آن را در AndroidManifest.xml از طریق ویژگی android:name ثبت کنید و پیکربندی StrictMode را اضافه کنید. ThreadPolicy.Builder همه آشکارسازها و همه انواع penalty را فعال می‌کند (به جز dialog — فقط زمانی کار می‌کند که دیباگر متصل باشد). VmPolicy.Builder آشکارسازهای نشت Activity و اشیاء Closable را اضافه می‌کند. برای پروژه‌های بزرگ (100+ صفحه) توصیه می‌شود VmPolicy را با penaltyDeath برای آشکارساز Activity Leaks پیکربندی کنید — سختگیرانه اما مؤثر.

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 استفاده کنید — اگر هر آزمایشی سیاست را نقض کند، برنامه با استثنا crash می‌کند. با Android Test Orchestrator ترکیب کنید تا هر آزمایش در یک فرآیند تمیز اجرا شود. برای تست‌های UI (Espresso, Compose Test) یک TestRule سفارشی بنویسید که نقض‌های StrictMode را رهگیری کرده و به assertion failure تبدیل کند. مثال: در @Before StrictMode را فعال کنید و در @After بررسی کنید که violation وجود نداشته باشد.

تنظیم آستانه‌ها

پیش‌فرض آستانه برای customSlowCall 2000 میلی‌ثانیه است، برای disk_read و disk_write — بدون آستانه (هر عملیاتی فعال می‌کند). از طریق setSlowCallDurationThreshold و setSlowIoDurationThreshold می‌توانید مقادیر خود را به میلی‌ثانیه تنظیم کنید. اگر برنامه شما به طور موجه SharedPreferences را در رشته اصلی می‌خواند (تنظیمات کوچک)، آستانه را به 10–20 میلی‌ثانیه افزایش دهید — این کار خواندن‌های سریع را فیلتر می‌کند اما خواندن‌های کند را باقی می‌گذارد.

بهترین روش‌های StrictMode

StrictMode ابزاری قدرتمند اما حساس است. پیکربندی نادرست منجر به میلیون‌ها هشدار کاذب می‌شود که باعث می‌شود توسعه‌دهندگان به آنها توجه نکنند. در زیر — روش‌های اثبات‌شده جمع‌آوری شده از تجربه تیم‌های بزرگ Android آورده شده است.

فقط در بیلدهای debug فعال کنید

این یک قانون آهنین است: StrictMode هرگز نباید در بیلدهای release فعال باشد. از پرچم BuildConfig.DEBUG یا buildConfigField سفارشی استفاده کنید. در بیلدهای release، بسیاری از کتابخانه‌های شخص ثالث به طور موجه عملیات را در رشته اصلی انجام می‌دهند (راه‌اندازی SDK، نوشتن کش)، و StrictMode هشدارهای کاذب ایجاد می‌کند. علاوه بر این، penaltyDialog در بیلد release به کاربر نهایی دیالوگ نشان می‌دهد — که غیرقابل قبول است.

از سه سطح سختگیری استفاده کنید

برای پروژه‌های کوچک (1–10 صفحه) penaltyLog را پیکربندی کنید — لاگ‌ها برای تحلیل دستی کافی است. برای پروژه‌های متوسط (10–50 صفحه) penaltyDeath را برای network و customSlowCalls اضافه کنید. برای پروژه‌های بزرگ (50+ صفحه) مجموعه کامل سیاست‌ها را با penaltyDeath در CI/CD فعال کنید و برای توسعه محلی — penaltyLog. این درجه‌بندی به توسعه‌دهنده با crashهای کاذب فشار نمی‌آورد اما کیفیت را در خط لوله به شدت کنترل می‌کند.

مدیریت false positives

برخی کتابخانه‌ها (Firebase, Crashlytics, Adjust) به طور موجه عملیات را در پس‌زمینه انجام می‌دهند که StrictMode ممکن است به اشتباه تشخیص دهد. راه‌حل‌ها: کتابخانه را از طریق penaltyListener به whitelist اضافه کنید، کتابخانه را به نسخه‌ای با جابجایی صریح به background thread به‌روزرسانی کنید، یا از StrictMode.vmPolicy استفاده کنید. در Android 11+ StrictMode.OnVmViolationListener برای فیلتر برنامه‌ریزی شده نقض‌ها بر اساس stacktrace اضافه شده است.

kotlin
// فیلتر کردن false positives از طریق 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
زمان بررسیRuntime (هنگام اجرای برنامه)Compile time (قبل از اجرا)Runtime (post-mortem)
چه چیزی را بررسی می‌کنددیسک، شبکه، نشت‌هاXML، کد، منابعCPU، حافظه، شبکه، انرژی
خودکارسازیCI/CD از طریق penaltyDeathGradle task + lint-baselineنیاز به تحلیل دستی دارد
عمقفقط رشته UI و نشت‌هاتحلیل استاتیک کدتصویر کامل عملکرد
هشدارهای کاذبمتوسط (وابسته به کتابخانه‌ها)کم (قوانین پیکربندی شده)ندارد (اندازه‌گیری‌های واقعی)

بهترین استراتژی — ترکیب هر سه رویکرد است: Android Lint خطاهای آشکار را در مرحله کامپایل می‌گیرد (مثلاً IdleHandler فراموش شده)، StrictMode مشکلات را در زمان اجرا تشخیص می‌دهد، و Android Profiler / Perfetto برای تحلیل عمیق هنگامی که دو ابزار اول جواب نمی‌دهند استفاده می‌شود. در پروژه‌های واقعی (Google Maps, Instagram) StrictMode در هفته دوم توسعه — بلافاصله پس از پیکربندی معماری پایه — پیاده‌سازی می‌شود.

نمونه کدهای StrictMode

دو سناریوی واقعی را بررسی می‌کنیم که در آنها StrictMode به تشخیص و رفع مشکلات عملکرد کمک می‌کند: خواندن SharedPreferences در رشته اصلی و نشت Activity از طریق callback ثبت‌نشده.

تشخیص 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 → VIOLATION!
        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 به دلیل مرجع استاتیک یا callback ثبت‌نشده همچنان در حافظه باقی مانده است. سناریوی معمول: ثبت EventBus یا LocationListener در onResume بدون فراخوانی unregister در onPause. VmPolicy stack trace را با اشاره به خطی که مرجع در آن ایجاد شده است خروجی می‌دهد.

kotlin
// ❌ نشت — callback لغو نشده
private var locationCallback: LocationCallback? = null

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

override fun onPause() {
    super.onPause()
    // فراموش کردید: locationManager.unregister(locationCallback)
}

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

آیا StrictMode برنامه را کند می‌کند؟

StrictMode واقعاً مقدار کمی overhead اضافه می‌کند — هر تماس سیستمی از نظر مطابقت با سیاست‌ها بررسی می‌شود. تأثیر بر عملکرد 1–3٪ در بیلد debug است و در release وجود ندارد (جایی که StrictMode غیرفعال است). هنگام فعال‌سازی detectAll در دستگاه‌های قدیمی (Android 6–8) overhead می‌تواند به 5٪ برسد، بنابراین توصیه می‌شود فقط سیاست‌های مورد نیاز را پیکربندی کنید.

آیا می‌توان از StrictMode با Jetpack Compose استفاده کرد؟

بله، StrictMode کاملاً با Jetpack Compose سازگار است. سیاست‌های disk و network در سطح فریم‌ورک، مستقل از UI-فریم‌ورک کار می‌کنند. علاوه بر این، در Compose بحرانی‌بودن مسدودیت‌های UI بیشتر است — Compose فریم‌ها را با فرکانس 120 FPS در دستگاه‌های با نرخ تازه‌سازی بالا بازپرداخت می‌کند، بنابراین 5 میلی‌ثانیه اضافی برای خواندن فایل محسوس‌تر می‌شود.

چرا StrictMode در خواندن SharedPreferences فعال نمی‌شود؟

از Android 8.1 (API 27) به بعد، SharedPreferences ممکن است از کش در حافظه استفاده کند — اگر فایل قبلاً خوانده شده باشد، خواندن مجدد StrictMode را فعال نمی‌کند. بررسی کنید که getSharedPreferences را برای اولین بار فراخوانی می‌کنید (خواندن سرد) و سیاست detectDiskReads فعال است. همچنین بررسی کنید که StrictMode در fragment بدون parent بازنویسی نشده باشد.

چگونه 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 assertion برای رشته اصلی استفاده کنید.

خلاصه

  • StrictMode — آشکارساز زمان اجرای مشکلات عملکرد در رشته اصلی Android
  • سیاست‌ها به ThreadPolicy (دیسک، شبکه) و VmPolicy (نشت حافظه) تقسیم می‌شوند
  • پیکربندی 10 خط کد در Application.onCreate با بررسی BuildConfig.DEBUG طول می‌کشد
  • برای CI/CD از penaltyDeath استفاده کنید — نقض سیاست منجر به crash برنامه می‌شود
  • StrictMode جایگزین نمی‌کند، بلکه مکمل Android Lint و Perfetto است
  • فیلتراسیون صحیح false positives — کلید استفاده مؤثر از ابزار
  • توصیه می‌شود StrictMode در هفته دوم توسعه پروژه پیاده‌سازی شود

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

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

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

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