במסמך הזה מוסבר איך לזהות שרשור שלא מגיב במקבץ של שגיאות ANR לזרום. השרשור שלא מגיב משתנה לפי סוג ה-ANR, כמו שמוצג בקטע הבא טבלה.
סוג שגיאת ANR | השרשור לא מגיב |
---|---|
ניתוב קלט | Thread ראשי |
אין חלון ממוקד | ה-thread הראשי. סוג ה-ANR הזה לא נגרם בדרך כלל על ידי משתמש חסום של שרשור. |
מקלט שידורים (סינכרוני) | השרשור פועל
onReceive()
זהו ה-thread הראשי, אלא אם ה-handler המותאם אישית ב-thread שאינו ראשי
צוין באמצעות
Context.registerReceiver |
מקלט שידורים (אסינכרוני) | צריך לבדוק בקוד איזה שרשור או מאגר שרשורים אחראים
לבצע את העבודה לעיבוד השידור אחרי
goAsync
נקראת. |
תם הזמן הקצוב לתפוגה של השירות | Thread ראשי |
התחלת שירות שפועל בחזית | Thread ראשי |
ספק התוכן לא מגיב | אחת משתי האפשרויות:
|
אין תגובה ל-
onStartJob
או
onStopJob |
Thread ראשי |
לפעמים השרשור לא מגיב בגלל בעיה בשרשור אחר או לעבד אותו. יכול להיות שלא תהיה תגובה מהשרשור הזה בגלל ההמתנה הבאה:
- נעילה שמופעלת על ידי שרשור אחר.
- קריאה איטית ב-Binder לתהליך שונה.
סיבות נפוצות לשרשורים שלא מגיבים
ריכזנו כאן סיבות נפוצות לשרשורים שלא מגיבים.
הפעלה איטית של Binder
למרות שרוב ההפעלות של קלסרים הן מהירות, "זנב ארוך" יכול להיות איטי מאוד. הדבר גדול יותר אם המכשיר נטען או ששרשור התשובה של קלסר איטיות, למשל עקב תחרות על נעילה, הרבה שיחות נכנסות מ-Binder או חומרה הזמן הקצוב לתפוגה של שכבת הפשטה (HAL).
אפשר לפתור את הבעיה על ידי העברת קריאות סינכרוניות ב-Binder לשרשורים ברקע ככל האפשר. אם השיחה חייבת להתבצע בשרשור הראשי, צריך לבדוק למה השיחה איטית. הדרך הטובה ביותר לעשות זאת היא באמצעות מעקבי Perfetto.
צריך לחפש את BinderProxy.transactNative
או את Binderproxy.transact
במקבצים.
המשמעות היא שמתקיימת שיחה ב-Binder. אחרי שתי השורות האלה, אפשר לראות
את ה-API של binder שנקרא. בדוגמה הבאה, הקריאה היא
IAccessibilityManager.addClient
main tid=123
...
android.os.BinderProxy.transactNative (Native method)
android.os.BinderProxy.transact (BinderProxy.java:568)
android.view.accessibility.IAccessibilityManager$Stub$Proxy.addClient (IAccessibilityManager.java:599)
...
הרבה קריאות רצופות ב-Binder
ביצוע קריאות רבות ב-Binder רצופות בלולאה צרות יכול לחסום שרשור עבור לתקופה ארוכה.
קלט/פלט (I/O) חוסם
אף פעם לא לחסום קלט/פלט (I/O) ב-thread הראשי. זהו אנטי-דפוס.
תחרות על נעילה
אם שרשור נחסם במהלך הגדרה של נעילה, זה עלול לגרום ל-ANR.
בדוגמה הבאה אפשר לראות שה-thread הראשי חסום כשמנסים להשיג lock:
main (tid=1) Blocked
Waiting for com.example.android.apps.foo.BarCache (0x07d657b7) held by
ptz-rcs-28-EDITOR_REMOTE_VIDEO_DOWNLOAD
[...]
at android.app.ActivityThread.handleStopActivity(ActivityThread.java:5412)
[...]
השרשור החסום שולח בקשת HTTP להורדת סרטון:
ptz-rcs-28-EDITOR_REMOTE_VIDEO_DOWNLOAD (tid=110) Waiting
at jdk.internal.misc.Unsafe.park(Native method:0)
at java.util.concurrent.locks.LockSupport.park(LockSupport.java:211)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquire(AbstractQueuedSynchronizer.java:715)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireSharedInterruptibly(AbstractQueuedSynchronizer.java:1047)
at java.util.concurrent.CountDownLatch.await(CountDownLatch.java:230)
at com.example.android.apps.foo.HttpRequest.execute(HttpRequest:136)
at com.example.android.apps.foo$Task$VideoLoadTask.downloadVideoToFile(RequestExecutor:711)
[...]
מסגרת יקרה
עיבוד של יותר מדי פריטים במסגרת אחת עלול לגרום ל-thread הראשי לא מגיב למשך הזמן של המסגרת, למשל:
- רינדור עם הרבה פריטים מיותרים שלא מופיעים במסך.
- שימוש באלגוריתם לא יעיל, כמו
O(n^2)
, במהלך רינדור של הרבה ממשקי משתמש רכיבים.
נחסם על ידי רכיב אחר
אם רכיב אחר, כמו מקלט שידורים, חוסם את ה-thread הראשי עבור יותר מחמש שניות, הדבר עלול לגרום לשגיאות ANR בקלט ולבעיות בעיות חמורות.
כדאי להימנע מעבודה קשה על ה-thread הראשי ברכיבי האפליקציה. הפעלת שידור בשרשורים אחרים, כשהדבר אפשרי.