כששרשור ה-UI של אפליקציה ל-Android נחסם למשך זמן רב מדי, מופעלת השגיאה "האפליקציה לא מגיבה" (ANR). תפקוד האפליקציה עוקב אחרי מדדי ANR ומדווח עליהם ב-Google Play Console כדי לעזור לכם לעקוב אחרי היציבות והתגובה של האפליקציה במכשירים שונים.
מדדים וערכי סף של תפקוד האפליקציה
התכונה "תפקוד האפליקציה" יכולה לעזור לכם לעקוב אחרי שיעור מקרי ה-ANR באפליקציה ולשפר אותו. במסגרת תפקוד האפליקציה נמדדים כמה שיעורי מקרי ה-ANR:
- שיעור מקרי ה-ANR: אחוז המשתמשים הפעילים ביום (DAU) שהושפעו מכל סוג של ANR.
- שיעור מקרי ה-ANR שהשפיעו על המשתמשים: אחוז המשתמשים הפעילים ביום (DAU) שהושפעו ממקרה ANR אחד לפחות. נכון לעכשיו, רק מקרי ANR מסוג
Input dispatching timed outנחשבים כמקרים שהשפיעו על המשתמשים. - שיעור של ריבוי מקרי ANR: אחוז המשתמשים הפעילים ביום (DAU) ששני מקרי ANR לפחות השפיעו עליהם.
משתמש פעיל ביום הוא משתמש ייחודי שמשתמש באפליקציה ביום אחד במכשיר אחד, אולי בכמה סשנים. אם משתמש משתמש באפליקציה שלכם ביותר ממכשיר אחד ביום אחד, כל מכשיר יתרום למספר המשתמשים הפעילים באותו יום.
שיעור מקרי ה-ANR שהשפיעו על המשתמשים הוא נתון בסיסי של תפקוד האפליקציה, כלומר הוא משפיע על יכולת הגילוי של האפליקציה ב-Google Play. המדד הזה חשוב כי מקרי ה-ANR שהוא סופר תמיד מתרחשים כשהמשתמשים משתמשים באפליקציה, וזה גורם להפרעה הכי גדולה.
ב-Play הוגדרו שני ספי התנהגות לא תקינה למדד הזה:
- סף ההתנהגות הלא תקינה הכולל: לפחות 0.47% מהמשתמשים הפעילים ביום נתקלים במקרה ANR שהשפיע על המשתמשים, בכל דגמי המכשירים.
- סף ההתנהגות הלא תקינה לכל מכשיר: לפחות 8% מהמשתמשים היומיים נתקלים ב-ANR שהשפיע על המשתמשים, בדגם מכשיר אחד.
אם האפליקציה חורגת מסף ההתנהגות הלא תקינה הכולל, סביר להניח שזה יפגע ביכולת הגילוי שלה בכל המכשירים. אם האפליקציה חורגת מסף ההתנהגות הלא תקינה בכל מכשיר במכשירים מסוימים, סביר להניח שזה יפגע ביכולת הגילוי שלה במכשירים האלה, ויכול להיות שתוצג אזהרה בדף האפליקציה בחנות.
תפקוד האפליקציה יכול להתריע לכם דרך Play Console אם באפליקציה שלכם יש יותר מדי מקרי ANR.
במסמכי התיעוד של Play Console מוסבר איך Google Play אוספת נתונים על תפקוד האפליקציה.
זיהוי הבעיה
אם האפליקציה פועלת בחזית כשמתרחשת שגיאת ANR, המערכת מציגה למשתמש תיבת דו-שיח, כמו שמוצג באיור 1. בתיבת הדו-שיח של ה-ANR, המשתמש יכול לבצע יציאה ידנית מהאפליקציה.
שגיאות ANR הן בעיה כי ה-thread הראשי של האפליקציה, שאחראי על עדכון ממשק המשתמש, לא יכול לעבד אירועי קלט של משתמשים או לצייר, וזה גורם לתסכול אצל המשתמשים. מידע נוסף על ה-thread הראשי של האפליקציה זמין במאמר סקירה כללית על תהליכים ו-threads.
שגיאת ANR מופעלת באפליקציה כשאחד מהתנאים הבאים מתקיים:
- הזמן הקצוב לשליחת קלט פג: אם האפליקציה לא הגיבה לאירוע קלט (כמו לחיצה על מקש או נגיעה במסך) תוך 5 שניות.
- הפעלת שירות: אם שירות שהוגדר באפליקציה לא יכול לסיים את ההפעלה של
Service.onCreateו-Service.onStartCommandאוService.onBindתוך כמה שניות. - לא בוצעה קריאה ל-
Service.startForeground: אם האפליקציה משתמשת ב-Context.startForegroundServiceכדי להפעיל שירות חדש שפועל בחזית, אבל השירות לא קורא ל-startForegroundתוך 5 שניות. - שידור של Intent: אם
BroadcastReceiverלא סיים לפעול בתוך פרק זמן מוגדר. אם לאפליקציה יש פעילות כלשהי בחזית, זמן הקצוב לתפוגה הוא 10 שניות. - אינטראקציות עם
JobScheduler: אםJobServiceלא חוזר מ-JobService.onStartJobאו מ-JobService.onStopJobתוך כמה שניות, או אם משימה שהמשתמש התחיל מתחילה והאפליקציה לא קוראת ל-JobService.setNotificationתוך כמה שניות אחרי שבוצעה קריאה ל-JobService.onStartJob. באפליקציות שמטרגטות ל-Android 13 ומטה, שגיאות ה-ANR הן שקטות ולא מדווחות לאפליקציה. באפליקציות שמטרגטות ל-Android 14 ומעלה, שגיאות ה-ANR הן מפורשות ומדווחות לאפליקציה.
בנוסף לתפקוד האפליקציה, אתם יכולים להשתמש בכלים אחרים כדי לזהות מקרי ANR בשטח, אבל כלים של צד שלישי לא יכולים לדווח על מקרי ANR ב-Android בגרסה 10 (רמת API 29) ומטה.
אבחון של מקרי ANR
יש כמה דפוסים נפוצים שכדאי לחפש כשמנתחים שגיאות ANR:
- האפליקציה מבצעת פעולות איטיות שכוללות קלט/פלט ב-thread הראשי.
- האפליקציה מבצעת חישוב ארוך ב-thread הראשי.
- ה-thread הראשי מבצע קריאת Binder סינכרונית לתהליך אחר, ולתהליך האחר לוקח הרבה זמן להחזיר תשובה.
- ה-thread הראשי חסום. הוא נמצא בהמתנה לבלוק מסונכרן לפעולה ארוכה שמתרחשת ב-thread אחר.
- ה-thread הראשי נמצא במבוי סתום עם thread אחר, בתהליך שלכם או באמצעות קריאת Binder. ה-thread הראשי לא רק ממתין לסיום של פעולה ארוכה, אלא נמצא במצב של מבוי סתום.
השיטות הבאות יכולות לעזור לכם לקבוע מה הגורם לשגיאות ה-ANR.
HealthStats
HealthStats מספק מדדים לגבי תקינות האפליקציה על ידי תיעוד של זמן המשתמש והמערכת הכולל, זמן המעבד (CPU), הרשת, נתוני הרדיו, זמן ההפעלה והכיבוי של המסך והתראות ההתעוררות. כך תוכלו למדוד את השימוש הכולל ב-CPU ואת התרוקנות הסוללה.
ניפוי באגים
Debug עוזר לבדוק אפליקציות ל-Android במהלך הפיתוח, כולל מעקב וספירת הקצאות כדי לזהות תקיעות ועיכובים באפליקציות.
אתם יכולים גם להשתמש ב-Debug כדי לקבל מוני זיכרון מקוריים ומוני זיכרון של זמן ריצה, ומדדי זיכרון שיכולים לעזור לכם לזהות את הזיכרון שבשימוש של תהליך מסוים.
ApplicationExitInfo
המאפיין ApplicationExitInfo זמין ב-Android 11 (רמת API 30) ומעלה, והוא מספק מידע על הסיבה ליציאה מהאפליקציה. האירועים האלה כוללים מקרי ANR, זיכרון נמוך, קריסות של אפליקציות, שימוש מוגזם ב-CPU, הפרעות למשתמשים, הפרעות למערכת ושינויים בהרשאות בתחילת ההפעלה.
מצב קפדני
שימוש ב-StrictMode עוזר לכם למצוא פעולות קלט/פלט מקריות בשרשור הראשי בזמן שאתם מפתחים את האפליקציה. אתם יכולים להשתמש ב-StrictMode ברמת האפליקציה או הפעילות.
הפעלת תיבות דו-שיח של ANR ברקע
ב-Android מוצגות תיבות דו-שיח של ANR לאפליקציות שבהן עיבוד ההודעה לכולם נמשך יותר מדי זמן, רק אם האפשרות הצגת כל השגיאות מסוג ANR מופעלת באפשרויות למפתחים במכשיר. לכן, תיבות דו-שיח של ANR ברקע לא תמיד מוצגות למשתמש, גם כשיש בעיות בביצועים של האפליקציה.
פרופיילינג של המעבד (CPU) ועקבות המערכת
משתמשים ב-Android Studio CPU Profiler כדי לתעד את עקבות המערכת ואת פעילות המעבד (CPU) בזמן שהמשחק פועל. בדיקה של ה-thread הראשי של Activity לצד ה-threads של העיבוד, האודיו והעבודה של מנוע המשחק עוזרת לזהות פריימים ארוכים, טעינות נכסים סינכרוניות ותחרות על משאבים ב-thread לפני שהם גורמים לשגיאת ANR.
איך שולפים קובץ מעקב
מערכת Android שומרת מידע על המעקב כשהיא נתקלת בשגיאת ANR. בגרסאות Android ישנות יותר, יש קובץ /data/anr/traces.txt אחד במכשיר. בגרסאות מתקדמות יותר של Android, יש כמה קובצי /data/anr/anr_*. אפשר לגשת לקובצי המעקב של ה-ANR ממכשיר או מאמולטור באמצעות Android Debug Bridge (adb) כמשתמש root:
adb root
adb shell ls /data/anr
adb pull /data/anr/<filename>
כדי ליצור דוח על באג ממכשיר פיזי, אפשר להשתמש באפשרות למפתחים "יצירת דוח על באג" במכשיר או בפקודה adb bugreport במחשב הפיתוח. מידע נוסף זמין במאמר תיעוד וקריאה של דוחות על באגים.
איך פותרים את הבעיות
אחרי שמזהים את הבעיה, אפשר להשתמש בטיפים שבקטע הזה כדי לפתור בעיות נפוצות.
קוד איטי ב-thread הראשי
מזהים את המקומות בקוד שבהם ה-thread הראשי של האפליקציה עסוק במשך יותר מ-5 שניות. חפשו את תרחישי השימוש החשודים במשחק ונסו לשחזר את ה-ANR.
במשחקים, השהיות בשרשור הראשי מתרחשות בדרך כלל כשמתבצעת פריסה סינכרונית של נכסים, הידור של תוכניות הצללה או אתחול כבד של ערך דינמי של המשחק בשרשור הראשי של Android Activity (למשל בתוך onCreate, onResume או קריאות חוזרות (callbacks) של JNI שמופעלות משרשור UI). העבירו את ההפעלה של לולאת המשחק, עיבוד הנכסים והאתחול של הסצנה ל-Thread ייעודי של מנוע, עיבוד או עובד ברקע, כדי שה-Thread הראשי יישאר פנוי לעיבוד קלט ואירועים במחזור החיים.
קלט/פלט (I/O) ב-thread הראשי
ביצוע פעולות קלט/פלט (I/O) ב-thread הראשי הוא סיבה נפוצה לפעולות איטיות ב-thread הראשי, שיכולות לגרום לשגיאות ANR.
מעבירים את כל פעולות הקריאה של חבילות הנכסים, הסריאליזציה של השמירות במשחק, הגישה ל-SharedPreferences והבקשות לאחזור מהרשת מה-thread הראשי של Activity ל-threads עובדים ברקע, כדי שזמן האחזור של האחסון או הרשת לא יחסום את שליחת הקלט.
מבוי סתום
מבוי סתום מתרחש כששרשור נכנס למצב המתנה כי משאב נדרש מוחזק על ידי שרשור אחר, שגם ממתין למשאב שמוחזק על ידי השרשור הראשון. אם ה-thread הראשי של האפליקציה נמצא במצב הזה, סביר להניח שיתרחשו שגיאות ANR.
במשחקים, מבוי סתום ותחרות על נעילה מתרחשים לעיתים קרובות בין ה-thread הראשי של Android Activity (שמטפל בקריאות חוזרות של מחזור חיים, משטח או קלט) לבין ה-threads של רינדור, לוגיקה או אודיו ב-C או ב-C++. הימנעו מהמתנות סינכרוניות בשרשור הראשי לנעילות של המנוע המקורי, והשתמשו בסדר נעילה עקבי או בתורי הודעות ללא נעילה כשאתם מתקשרים בין JNI לבין שרשורי המנוע.
מבוי סתום הוא תופעה שנחקרה רבות במדעי המחשב, ויש אלגוריתמים למניעת מבוי סתום שבהם אפשר להשתמש כדי להימנע ממצבים כאלה.
למידע נוסף, קראו את המאמרים מבוי סתום ואלגוריתמים למניעת מבוי סתום בוויקיפדיה.
מקלטי שידורים איטיים
אפליקציות יכולות להגיב להודעות לכולם, כמו הפעלה או השבתה של מצב טיסה או שינוי בסטטוס הקישוריות, באמצעות מקלטי שידור. שגיאת ANR מתרחשת כשלוקח לאפליקציה יותר מדי זמן לעבד את ההודעה לכולם.
שגיאת ANR מתרחשת במקרים הבאים:
- מקלט שידור לא סיים להפעיל את השיטה
onReceiveשלו בפרק זמן סביר. - מקלט שידור קורא ל-
goAsyncולא מצליח לקרוא ל-finishבאובייקטPendingResult.
האפליקציה צריכה לבצע רק פעולות קצרות ב-method onReceive של BroadcastReceiver. עם זאת, אם האפליקציה דורשת עיבוד מורכב יותר כתוצאה מהודעה לכולם, כדאי לדחות את המשימה ל-Thread עובד ברקע אם היא צפויה להימשך כמה שניות לכל היותר, או ל-WorkManager אם היא צפויה להימשך יותר מכמה שניות.
GameActivity
הספרייה GameActivity הפחיתה את מספר מקרי ה-ANR במקרי בוחן של משחקים ואפליקציות שנכתבו ב-C או ב-++C. אם תחליפו את פעילות ה-Native הקיימת ב-GameActivity, תוכלו להפחית את החסימה של שרשור ה-UI ולמנוע חלק ממקרי ה-ANR.
אם אתם מפתחים את המשחק באמצעות Unity, כדאי לעיין במאמרים ניפוי באגים של מקרי ANR במשחקי Unity, מקרי ANR נפוצים במשחקי Unity והמרת סמלים של קריסות ומקרי ANR ב-Android למשחקי Unity.
מידע נוסף על שגיאות ANR זמין במאמר שמירה על תגובתיות האפליקציה. למידע נוסף על שרשורים, ראו שיפור הביצועים באמצעות שרשורים.
מומלץ בשבילך
- הערה: טקסט הקישור מוצג כש-JavaScript מושבת
- יציאות רבות מדי ממצב שינה