תארו לעצמכם: משתמש פותח אפליקציה, גולל בפיד—והמסך קופא לחצי שנייה. נגיעות לא נרשמות, האנימציה נתקעת. אחרי כמה שניות, הכל חוזר לקדמותו, אבל הרושם נשאר. שיעור החזרות יורד, בעוד ש-Crashlytics נשאר שקט—אין קריסה רשמית. זהו תרחיש קלאסי של App Hang (ב-iOS) או UI Freeze (באנדרואיד). אנו עוזרים להגדיר ניטור לתקיעות כאלה, לזהות צווארי בקבוק ולחסל אותם. חיסכון בתקציב מהפחתת תקיעות: עד 30% הפחתה בעומס על ה-thread הראשי.
למה דוחות קריסה רגילים מפספסים תקיעות
App Hangs ו-UI Freezes לא זורקים חריגות—האפליקציה לא קורסת, אבל מפסיקה להגיב לפעולות המשתמש. ה-Watchdog של iOS מסיים את התהליך רק אחרי 4–8 שניות של תקיעה, ו-ANR של אנדרואיד אחרי 5 שניות. תקיעות קצרות של 200–500 אלפיות השנייה לא מזוהות אבל הורסות את חוויית המשתמש. דוחות קריסה רגילים (Crashlytics, Firebase Crashlytics) לוכדים רק חריגות ואותות, ולכן יש צורך במעקב נפרד כדי לזהות תקיעות. אנו משתמשים בשלוש גישות: watchdog thread, מדדי פריימים ומעקב אחר משך משימות.
איזה סף תקיעה להגדיר לסביבת ייצור
אנו ממליצים על 250 אלפיות השנייה ל-iOS ו-200 אלפיות השנייה לאנדרואיד. סף נמוך יותר (100 אלפיות השנייה) ייצור רעש מעיכובים אקראיים, בעוד שסף גבוה יותר (500 אלפיות השנייה) יפספס תקיעות מורגשות. ניתן להתאים את הסף לכל מסך אם צריך—לדוגמה, לרשימות מורכבות אפשר להשתמש ב-300 אלפיות השנייה. Sentry ו-Datadog מאפשרים הגדרה לפי סף. עלות ההתקנה משתלמת דרך שיפור חוויית המשתמש.
כלי זיהוי
| כלי | פלטפורמה | סף | זמן השהיה | הערות |
|---|---|---|---|---|
| MetricKit | iOS | 250 אלפיות השנייה | 24 שעות | מובנה במערכת ההפעלה, אין צורך ב-SDK, אבל לא בזמן אמת |
| Sentry App Hang | iOS, אנדרואיד | מ-100 אלפיות השנייה | זמן אמת | Watchdog thread, הקלטת מחסנית קריאות |
| FrameMetricsAggregator | אנדרואיד | >16 אלפיות השנייה | לפי בקשה | סטטיסטיקות פריימים תקועים בסביבת ייצור |
| Datadog RUM Long Task | iOS, אנדרואיד | מ-100 אלפיות השנייה | זמן אמת | לוחות מחוונים וקיבוץ לפי מסך |
Sentry מזהה תקיעות בזמן אמת, בעוד של-MetricKit יש השהיה של 24 שעות. Sentry מזהה תקיעות פי 100 מהר יותר, מה שחשוב לתיקונים חמים. תיעוד MetricKit מאשר את ההשהיה של עד 24 שעות.
גורמים נפוצים לתקיעות
| פלטפורמה | גורם אופייני | משך |
|---|---|---|
| iOS | פענוח תמונות ב-thread הראשי | 30–80 אלפיות השנייה |
| iOS | טעינת נתונים סינכרונית | 100–300 אלפיות השנייה |
| אנדרואיד | Recomposition מיותר ב-Compose | 50–200 אלפיות השנייה |
| אנדרואיד | השהאות GC | 10–50 אלפיות השנייה |
נתונים אלה מבוססים על פרויקטים אמיתיים עם קהלים של 100k+ משתמשים.
iOS — זיהוי App Hang עם Sentry
SentrySDK.start { options in
options.dsn = "https://[email protected]/project"
options.enableAppHangTracking = true
options.appHangTimeoutInterval = 0.25 // 250ms
}Sentry מפעיל watchdog thread ששולח ping ל-thread הראשי כל 100 אלפיות השנייה. אם אין תגובה בתוך SentrySDK.start { options in options.dsn = "https://[email protected]/project" options.enableAppHangTracking = true options.appHangTimeoutInterval = 0.25 // 250ms } , הוא לוכד את המחסנית דרך appHangTimeoutInterval ומגיש אותה כ-Issue.
אנדרואיד — FrameMetricsAggregator
val frameMetrics = FrameMetricsAggregator(FrameMetricsAggregator.JANK_DATA)
frameMetrics.add(activity)
// Позже:
val metrics = frameMetrics.metrics
metrics?.get(FrameMetricsAggregator.JANK_INDEX)?.let { jankArray ->
val jankyFrames = jankArray.size
}FrameMetricsAggregator מתאים לסביבת ייצור: קל משקל, מספק סטטיסטיקות פריימים תקועים. לאבחון מקומי מעמיק, השתמשו ב-Perfetto.
הגדרת ניטור ב-Datadog
RUM.enable(with: RUM.Configuration( applicationID: "your-rum-app-id", longTaskThreshold: 0.1 // 100ms )) בלוח המחוונים של Datadog, בנו widget:
count:rum.long_task{env:production,service:ios-app} group_by: @view.name visualize_as: top_list זה חושף אילו מסכים סובלים מהכי הרבה תקיעות.
מאיפה מגיעות תקיעות
ב-iOS, המקור הנפוץ ביותר לתקיעות קצרות הוא קריאות סינכרוניות ב-thread הראשי בתגובה לאירוע ממשק משתמש:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "ProductCell", for: indexPath) as! ProductCell
cell.imageView?.image = UIImage(data: product.imageData)
return cell
}backtrace_thread מפענח JPEG/PNG באופן סינכרוני. ב-iPhone SE עם תמונה של 1900x1200, זה חוסם את ה-thread הראשי ל-30–80 אלפיות השנייה.
באנדרואיד, גורם מרכזי במסכי Compose הוא recomposition מיותר ב-val frameMetrics = FrameMetricsAggregator(FrameMetricsAggregator.JANK_DATA) frameMetrics.add(activity) // Позже: val metrics = frameMetrics.metrics metrics?.get(FrameMetricsAggregator.JANK_INDEX)?.let { jankArray -> val jankyFrames = jankArray.size } :
@Composable fun ProductList(products: List<Product>) {
LazyColumn {
items(products) { product ->
ProductCard(product)
}
}
}החלפת RUM.enable(with: RUM.Configuration( applicationID: "your-rum-app-id", longTaskThreshold: 0.1 // 100ms )) ב-count:rum.long_task{env:production,service:ios-app} group_by: @view.name visualize_as: top_list (מ-kotlinx.collections.immutable) או שימוש ב-annotation func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "ProductCell", for: indexPath) as! ProductCell cell.imageView?.image = UIImage(data: product.imageData) return cell } מבטלת recompositions מיותרים.
מה כלול בעבודה שלנו
- שילוב Sentry עם
UIImage(data:)וסף של 250 אלפיות השנייה - הגדרת מנוי MetricKit לאבחון יומי
- הפעלת מעקב Datadog RUM Long Task עם סף של 100 אלפיות השנייה
- באנדרואיד, הגדרת FrameMetricsAggregator ל-Activities מרכזיים
- בניית לוח מחוונים של מסכים עם המספר הגבוה ביותר של Long Tasks
- ניתוח מחסניות קריאות לזיהוי גורמים ספציפיים
- דוח כתוב עם המלצות אופטימיזציה
הניסיון שלנו
יש לנו ניסיון רב בפרויקטים ניידים. הגדרנו ניטור ל-20+ אפליקציות עם קהלים של 100k+ משתמשים. אנו משתמשים בגישות מודרניות: MetricKit, Sentry, Datadog, Apple App Hang, Android ANR.
ציר זמן
הגדרה בסיסית דרך Sentry ו-Datadog: 4–8 שעות. אבחון מלא עם MetricKit ומדדים מותאמים אישית לכל מסך: 1–2 ימים. המחיר נקבע לאחר ניתוח.
צרו קשר כדי לבחור את הכלים הטובים ביותר לפרויקט שלכם. בקשו הגדרת ניטור—וקבלו ערובה לביצועים.







