کند شدن در توسعه — چیست، دلایل و روش‌های بهینه‌سازی

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

کند شدن — توصیف کاربر از وضعیتی است که برنامه موبایل کند و ناپایدار کار می‌کند: گاهی عادی پاسخ می‌دهد، گاهی ناگهان برای چند ثانیه قفل می‌کند. در زمینه فنی «کند شدن» به معنای ترکیبی از لگ‌ها و ریزهنگ‌هایی است که توسط مکث‌های مکرر GC، مسدود شدن رشته اصلی توسط عملیات همزمان و ساختارهای داده غیربهینه ایجاد می‌شود. طبق Android Performance Benchmarking Guide، کاهش زمان پاسخ از 300 ms به 100 ms retention کاربران را 25% افزایش می‌دهد. تشخیص کند شدن نیاز به ترکیب پروفایل‌سازی CPU و Memory با تحلیل دفعات جمع‌آوری زباله دارد.

نکات اصلی

  • کند شدن — کاهش سرعت نامنظم برنامه که با عملکرد عادی متناوب می‌شود
  • دلایل اصلی — مکث‌های مکرر GC، عملیات همزمان در رشته UI، حجم زیاد داده در آداپترها بدون صفحه‌بندی
  • تشخیص نیاز به CPU Profiler برای یافتن قفل‌ها و Memory Profiler برای تحلیل دفعات و مدت GC دارد
  • رفع شامل پیاده‌سازی صفحه‌بندی (Paging 3)، بهینه‌سازی کوئری‌های SQL از طریق Room و انتقال وظایف سنگین به WorkManager است
  • پیشگیری — Benchmark Baseline Profiles، کامپایل AOT، حداقل‌سازی تخصیص در مسیرهای داغ کد

«کند شدن» در توسعه موبایل به چه معناست

کند شدن — یک اصطلاح غیررسمی است که کاربران با آن عملکرد ذهنی کند برنامه را توصیف می‌کنند. برخلاف لگ که به صورت تأخیر ثابت ظاهر می‌شود، کند شدن هنگ‌های نامنظم است: برنامه می‌تواند چند ثانیه عالی کار کند و سپس 1–3 ثانیه «فکر کند».

ویژگی فنی پدیده

از دیدگاه پروفایل‌سازی، کند شدن به صورت یک سری فریم‌های از دست رفته (jank) با تأخیرهای پیک بیش از 100 ms ظاهر می‌شود. در نمودار FPS این به صورت افت‌های شدید دیده می‌شود: 60 → 20 → 55 → 10 فریم در ثانیه. برخلاف لگ با FPS یکنواخت پایین، کند شدن تغییرپذیری آشکاری دارد.

ادراک کاربر

وقتی برنامه کند می‌شود، کاربر منطق کندی را درک نمی‌کند: صفحه می‌تواند نرم اسکرول شود و سپس ناگهان برای یک ثانیه متوقف شود. این باعث ناامیدی و کاهش اعتماد به برنامه می‌شود. طبق Google، 53% کاربران سایت یا برنامه را ترک می‌کنند اگر بارگذاری بیش از 3 ثانیه طول بکشد.

دلایل کندی ناگهانی در برنامه‌ها

ماهیت نامنظم کند شدن نشان می‌دهد که مشکل ناشی از عوامل رویدادی است، نه بارگذاری دائمی. بیایید سناریوهای معمول را بررسی کنیم.

مکث‌های GC هنگام تخصیص اشیا

در Android در محیط ART، جمع‌آوری زباله همه رشته‌های برنامه را متوقف می‌کند. اگر در کد اشیای موقت زیادی ایجاد شود — مثلاً در هر فراخوانی onBindViewHolder یک String جدید از طریق اتصال ایجاد شود — GC بیشتر اجرا می‌شود. مکث بسته به اندازه heap و نسل اشیا می‌تواند 5–50 ms طول بکشد. کاربر این را به عنوان «فکر کردن» ناگهانی احساس می‌کند.

کوئری‌های همزمان SQL در رشته UI

Room در Android و Core Data در iOS از کوئری‌های ناهمزمان پشتیبانی می‌کنند، اما توسعه‌دهندگان اغلب برای سادگی getValue() فراخوانی می‌کنند یا کوئری را از طریق runBlocking اجرا می‌کنند. SELECT سنگین با join روی جدول 10 000 ردیفی می‌تواند 200–500 ms طول بکشد و UI را کاملاً مسدود کند.

دکدگذاری تصویر بدون downscale

بارگذاری تصویر دوربین (12 مگاپیکسل، 4000x3000 px) بدون مقیاس‌بندی تا 200 ms برای دکدگذاری به Bitmap طول می‌کشد. اگر تصاویر به صورت ناهمزمان بارگذاری شوند اما بدون پول رشته با محدودیت، اجرای همزمان 5–6 دکدگذاری می‌تواند CPU را بیش از حد بارگذاری کرده و باعث کندی‌های سرگردان شود.

  • Android — اتصال رشته‌ها در حلقه‌ها، ایجاد اشیا در مسیرهای داغ، Bitmap بدون inSampleSize
  • iOS — پول‌های autorelease با تعداد زیادی اشیا، imageWithContentsOfFile بدون مقیاس‌بندی، URLSession همزمان
  • Cross-platform — تجزیه JSON در رشته UI، بارگذاری داده در رشته اصلی با انتظار پاسخ سرور

چگونه هنگ کردن را در Android و iOS تشخیص دهیم

تشخیص کندی‌های نامنظم دشوارتر از تشخیص لگ‌های ثابت است، زیرا مشکل ممکن است در هر اجرا تکرار نشود. نیاز به جمع‌آوری آمار در یک دوره طولانی است.

Memory Profiler با ثبت رویدادهای GC

Android Studio Memory Profiler نه تنها استفاده از حافظه، بلکه رویدادهای GC را نیز نشان می‌دهد: دفعات، نوع (Concurrent، Full)، مدت. اگر GC در حالت سکون بیش از 1 بار در 5 ثانیه رخ دهد — این نشانه تخصیص بیش از حد است. ثبت heap dump در لحظه کند شدن امکان دیدن اشیایی که حافظه را اشغال می‌کنند فراهم می‌کند.

Xcode Instruments با Allocation Tracking

در iOS از الگوی Allocations در Instruments برای ردیابی ایجاد و آزادسازی اشیا استفاده کنید. نسل‌ها (Generations) را فعال کنید — آنها امکان گرفتن عکس‌هایی از heap بین عملیات و دیدن اشیایی که در حافظه باقی می‌مانند را فراهم می‌کنند. اشیای پایدار (Persistent objects) که آزاد نمی‌شوند — منبع انباشت حافظه و مکث‌های بعدی هستند.

API JankStats در Android

JankStats — کتابخانه Android که معیارهای فریم‌های از دست رفته را در زمان واقعی جمع‌آوری می‌کند. هر jank را به سناریوی فعلی (مثلاً «اسکرول لیست»، «باز کردن صفحه») متصل می‌کند که این امکان را می‌دهد بفهمیم کند شدن دقیقاً در کدام عمل رخ می‌دهد.

مثال ادغام JankStats برای ردیابی هنگ‌ها در Android:

kotlin
class MainActivity : AppCompatActivity() {
    private lateinit var jankStats: JankStats

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        jankStats = JankStats.create(this.window.decorView) { frameData ->
            if (frameData.isJank()) {
                Log.w("Jank", "Duration=${frameData.durationMs}ms")
            }
        }
    }
}

روش‌های رفع کندی کار

رفع کند شدن نیاز به کار هدفمند با هر علت دارد. راه‌حل جهانی وجود ندارد — نیاز به تحلیل پروفایل‌های عملکرد خاص است.

پیاده‌سازی صفحه‌بندی از طریق Paging 3

اگر لیست شامل 1000+ عنصر است و همه یکباره بارگذاری می‌شوند — این کند شدن تضمینی است. Paging 3 در Android و NSFetchedResultsController در iOS داده‌ها را در حین اسکرول به صورت تکه‌ای بارگذاری می‌کنند. کاربر فقط 10–20 عنصر اول را می‌بیند، بقیه در پس‌زمینه بارگذاری می‌شوند.

بهینه‌سازی کوئری‌های SQL و ایندکس‌ها

Room امکان پروفایل‌سازی کوئری‌ها را از طریق Inspection Tool در Android Studio فراهم می‌کند: زمان اجرا، تعداد ردیف‌های برگشتی و plan کوئری قابل مشاهده است. افزودن ایندکس به ستون‌های WHERE و ORDER BY می‌تواند زمان کوئری را از 300 ms به 5 ms کاهش دهد. در iOS بررسی مشابه توسط Core Data Profiler در Instruments انجام می‌شود.

انتقال وظایف به WorkManager

همگام‌سازی‌های پس‌زمینه، بارگذاری فایل‌ها، پردازش داده‌ها — همه اینها باید از طریق WorkManager (Android) یا Background Tasks (iOS) انجام شوند. اگر همگام‌سازی در رشته UI اجرا شود، برنامه در طول اجرا کند می‌شود. WorkManager اجرا را در رشته پس‌زمینه با در نظر گرفتن وضعیت باتری و شبکه تضمین می‌کند.

مثال همگام‌سازی پس‌زمینه از طریق WorkManager در Android:

kotlin
class SyncWorker(context: Context, params: WorkerParameters)
    : CoroutineWorker(context, params) {

    override suspend fun doWork(): Result {
        return try {
            Log.d("Sync", "همگام‌سازی داده در رشته پس‌زمینه")
            syncData()
            Result.success()
        } catch (e: Exception) {
            Result.retry()
        }
    }
}

پیشگیری از کند شدن در مرحله توسعه

می‌توان از کند شدن در مرحله نوشتن کد با پیروی از اصول کار مؤثر با حافظه و رشته‌ها جلوگیری کرد.

Baseline Profiles برای کامپایل AOT

Baseline Profiles — فهرستی از کلاس‌ها و متدهایی است که Android از قبل (AOT) کامپایل می‌کند، نه JIT. بدون پروفایل، هر صفحه جدید در اولین باز شدن کامپایل می‌شود و باعث تأخیر 100–500 ms می‌شود. Baseline Profile را برای صفحات کلیدی آماده کنید و تولید را در Gradle از طریق baseline-profile-gradle-plugin فعال کنید.

حداقل‌سازی تخصیص در مسیرهای داغ

Hot path — کدی است که در هر فریم اجرا می‌شود: onBindViewHolder، draw، layoutSubviews. از ایجاد اشیا در این متدها خودداری کنید: از پول اشیا استفاده کنید، به جای اتصال از StringBuilder استفاده کنید، رشته‌های فرمت شده و فرمت‌ها را کش کنید. هر تخصیص اضافی GC بعدی را نزدیک‌تر می‌کند.

پروفایل‌سازی از طریق Baseline Profiles در CI

به pipeline CI اجرای Macrobenchmark با سناریوی اسکرول لیست و باز کردن صفحه اضافه کنید. آستانه تعیین کنید: صدک 99 زمان فریم نباید از 16 ms تجاوز کند. اگر آستانه افزایش شود — build تا بهینه‌سازی رد می‌شود.

  • Android — Baseline Profiles، Macrobenchmark، JankStats، StrictMode با penaltyDeath
  • iOS — MetricKit، os_signpost، XCTMetric، Main Thread Checker در طرح Debug
  • رویکرد کلی — پروفایل‌سازی منظم، بازبینی کد با تمرکز بر تخصیص در مسیرهای داغ

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

کند شدن چه تفاوتی با لگ معمولی دارد؟

لگ — تأخیر ثابت است (مثلاً 200 ms برای هر کلیک). کند شدن — نامنظم است: برنامه عادی کار می‌کند، سپس ناگهان 1–3 ثانیه کند می‌شود، سپس دوباره عادی. علت — عوامل رویدادی مانند مکث‌های GC یا کوئری‌های همزمان به پایگاه داده.

چگونه دفعات مکث‌های GC را در Android اندازه‌گیری کنیم؟

از Memory Profiler در Android Studio استفاده کنید: برگه Memory رویدادهای GC را با مدت نشان می‌دهد. برای نظارت تولیدی، Firebase Performance Monitoring را با تریس‌های سفارشی متصل کنید. در iOS Malloc Debug را فعال کنید و نسل‌های تخصیص را در Instruments علامت‌گذاری کنید.

آیا کند شدن می‌تواند توسط کوئری‌های شبکه ایجاد شود؟

به طور غیرمستقیم — بله. اگر پاسخ سرور با تأخیر می‌رسد و UI به طور همزمان منتظر آن است، برنامه قفل می‌کند. اگر کوئری ناهمزمان است اما پردازش پاسخ در رشته UI انجام می‌شود — این نیز باعث کند شدن می‌شود. راه‌حل — پردازش ناهمزمان با coroutine و نشانگرهای پیشرفت.

Kotlin Multiplatform چگونه بر عملکرد تأثیر می‌گذارد؟

در صورت استفاده نادرست KMP می‌تواند اشیای wrapper اضافی برای قابلیت همکاری تولید کند. در iOS این دفعات تخصیص و در نتیجه مکث‌های ARC را افزایش می‌دهد. از @ObjCName استفاده کنید، expect/actual را بهینه کنید و از فراخوانی‌های مکرر کد مشترک از مسیرهای داغ UI خودداری کنید.

آیا افزایش اندازه heap در Android کمک می‌کند؟

افزایش heap از طریق android:largeHeap="true" GC را به تأخیر می‌اندازد اما علت تخصیص را از بین نمی‌برد. وقتی GC در نهایت اجرا شود، مکث طولانی‌تر خواهد بود زیرا باید اشیای بیشتری پیمایش شود. راه‌حل — کاهش تعداد تخصیص‌ها، نه گسترش heap.

خلاصه

  • کند شدن — کاهش سرعت نامنظم برنامه ناشی از عوامل رویدادی (مکث‌های GC، کوئری‌های همزمان، دکدگذاری تصاویر)
  • تشخیص نیاز به Memory Profiler، JankStats در Android و Allocation Tracking در Instruments در iOS دارد
  • دلایل اصلی — مکث‌های مکرر GC، نبود صفحه‌بندی، کوئری‌های SQL غیربهینه و پردازش همزمان در رشته UI
  • رفع — Paging 3، WorkManager، بهینه‌سازی ایندکس‌های پایگاه داده، مقیاس‌بندی تصاویر و حداقل‌سازی تخصیص
  • پیشگیری — Baseline Profiles، Macrobenchmark، StrictMode، بازبینی کد با بررسی مسیرهای داغ
  • ابزارها — JankStats، Firebase Performance، MetricKit برای نظارت تولیدی بر کندی‌ها
  • توصیه: اجرای منظم Macrobenchmark در CI با آستانه 16 ms برای صدک 99 فریم‌ها را پیاده‌سازی کنید

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

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

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

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