שגיאות מסוג 'האפליקציה לא מגיבה' (ANR) מתרחשות כששרשור ממשק המשתמש של האפליקציה לא מגיב במשך יותר מ-5 שניות. מידע נוסף על מקרי ANR ועל אבחון שלהם זמין במסמכי העזרה של Android.
בנוסף, Crashlytics יכול לעזור לכם לאתר שרשורים ספציפיים עם בעיות. אנחנו מנתחים את שגיאות ה-ANR, ולאחר מכן מתייגים את השרשור הרלוונטי במרכז הבקרה Crashlytics כדי לספק טיפים לניפוי הבאגים של שגיאת ה-ANR.
בקטעים הבאים בדף הזה מוסבר מה המשמעות של כל תג ANR, מוצגת דוגמה ל-ANR עם התג הזה ומופיע פתרון מומלץ לניפוי הבאגים של ה-ANR.
Triggered ANR
תג Triggered ANR
מתווסף לשרשור שנחסם במשך זמן רב מדי וגרם ל-ANR.
השרשור הבעייתי יכול להיות השרשור הראשי של האפליקציה, או כל שרשור שנמצא לא מגיב. עם זאת, יכול להיות ששרשור המסומן בתג Triggered ANR
הוא הסיבה האמיתית ל-ANR, ויכול להיות שלא. כדי לספק תובנות לניפוי באגים ולפתרון הבעיות האלה, Crashlytics מתייג גם את כל השרשורים האחרים שקשורים ל-ANR. בקטעים הבאים בדף מוסבר על תגים אחרים שאפשר להחיל על שרשור.
Deadlocked
כל השרשורים שנמצאו מעורבים בנעילה מרומזת שהובילה ל-ANR מסומנים בתג Deadlocked
.
נעילה מרומזת מתרחשת כשחוט נכנס למצב המתנה כי משאב נדרש נמצא בחזקתו של חוט אחר, שגם הוא מחכה למשאב שנמצא בחזקתו של החוט הראשון. אם ה-thread הראשי של האפליקציה נמצא במצב כזה, סביר להניח שיהיו שגיאות ANR.
הצגת דוגמה
אלה שני השרשוריים שמעורבים בנעילה מרובת משתמשים:
main (unknown): tid=1 systid=1568
com.android.server.pm.PackageManagerService$PackageManagerInternalImpl.getPackage(PackageManagerService.java:22701)
com.android.server.pm.PackageManagerService$PackageManagerInternalImpl.filterOnlySystemPackages(PackageManagerService.java:22787)
...
com.android.server.SystemServer.main(SystemServer.java:368)
java.lang.reflect.Method.invoke(Native method)
com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:517)
com.android.internal.os.ZygoteInit.main(ZygoteInit.java:934)
ActivityManager (unknown): tid=21 systid=1902
com.android.server.pm.PackageManagerService.getPackageSetting(PackageManagerService.java:23618)
com.android.server.pm.PackageManagerService.getPackageUid(PackageManagerService.java:4542)
...
android.os.Handler.handleCallback(Handler.java:907)
android.os.Handler.dispatchMessage(Handler.java:99)
android.os.Looper.loop(Looper.java:216)
android.os.HandlerThread.run(HandlerThread.java:67)
com.android.server.ServiceThread.run(ServiceThread.java:44)
המלצה
בודקים את השרשורים שמעורבים בנעילה ודואגים לבדוק את המשאבים או המנעולים שהשרשורים האלה רכשו. תוכלו לקרוא על פתרונות אפשריים במאמרים נעילה מרובת משתמשים (deadlock) ואלגוריתמים למניעת נעילה מרובת משתמשים (deadlock).
IO Root blocking
כל thread שהפעיל פעולות קלט/פלט איטיות וחסם את ה-thread Triggered ANR
יסומן בתג IO Root blocking
. אם השרשור Triggered ANR
לא נחסם על ידי שרשורים אחרים, השרשור IO Root blocking
הוא גם שרשור Root blocking
.
הצגת דוגמאות
Thread main(THREAD_STATE_TIMED_WAITING)
sun.misc.Unsafe.park( Unsafe.java:0 )
java.util.concurrent.locks.LockSupport.parkNanos( LockSupport.java:230 )
android.database.sqlite.SQLiteConnectionPool.waitForConnection( SQLiteConnectionPool.java:756 )
...
android.app.ActivityThread.main( ActivityThread.java:8192 )
Thread main(THREAD_STATE_NATIVE_WAITING)
Syscall
art::ConditionVariable::WaitHoldingLocks(art::Thread*)
art::GoToRunnable(art::Thread*)
art::JniMethodEnd(unsigned int, art::Thread*)
libcore.io.Linux.fdatasync( Linux.java:0 )
libcore.io.ForwardingOs.fdatasync( ForwardingOs.java:105 )
...
java.io.RandomAccessFile.write( RandomAccessFile.java:559 )
...
android.app.ActivityThread.main( ActivityThread.java:8192 )
המלצה
באופן כללי, אסור שהאפליקציה תבצע פעולות קלט/פלט יקרות ב-thread הראשי. אם ה-thread הראשי הוא IO Root blocking
, אפשר גם להשתמש במצב קפדני כדי לזהות פעולות קלט/פלט לא מכוונות שמתרחשות ב-thread הראשי.
Root blocking
כל שרשור שחסם את השרשור המסומן בתג Triggered ANR
יסומן בתג Root blocking
. אם שרשור מסומן גם בתג Root blocking
וגם בתג Triggered ANR
, סימן שאין שרשורים אחרים שחוסמים את אותו שרשור.
אם יש חוטים של Triggered ANR
שהמתינו (אולי באופן טרנזיטיבי) לחוטים אחרים, הם Root blocking
. יכולות להיות סיבות שונות לכך ששרשור הוא הגורם העיקרי ל-ANR.
הצגת דוגמאות
הנה כמה דוגמאות על סמך מצב השרשור:
Thread main(THREAD_STATE_RUNNABLE)
android.os.Parcel.createTypedArray( Parcel.java:3086 )
android.content.pm.PackageInfo.<init>( PackageInfo.java:546 )
...
android.app.ActivityThread$H.handleMessage( ActivityThread.java:2166 )
android.os.Handler.dispatchMessage( Handler.java:106 )
android.os.Looper.loop( Looper.java:246 )
android.app.ActivityThread.main( ActivityThread.java:8633 )
Thread main(THREAD_STATE_BLOCKED)
DBHelper.runOnDB( DBHelper.java:97 )
DBHelper.runDb( DBHelper.java:125 )
...
java.lang.reflect.Method.invoke( Method.java:0 )
EventBus.invokeSubscriber( EventBus.java:510 )
postToSubscription( EventBus.java:437 )
...
android.os.Handler.handleCallback( Handler.java:938 )
android.os.Handler.dispatchMessage( Handler.java:99 )
android.os.Looper.loop( Looper.java:268 )
android.app.ActivityThread.main( ActivityThread.java:7904 )
המלצה
צמצום העבודה המאומצת של המעבד בשרשור הראשי. שימוש בשרתי עבודה או בשרתי רקע לביצוע משימות שמתבצעות בסיוע מעבד.
צמצום של עבודה אינטנסיבית של קלט/פלט, כמו טעינה ממסד נתונים, ב-thread הראשי.
Unknown root cause
שרשור מסומן בתג Unknown root cause
אם הוא השרשור שהפעיל את שגיאת ה-ANR, אבל לא היה פעיל בתהליך כשהיא התרחשה. למערכת Crashlytics אין מספיק מידע כדי לקבוע את שורש הבעיה. אין סיבה ברורה לכך שה-ANR הזה התרחש.
הצגת דוגמה
Thread main(THREAD_STATE_NATIVE_WAITING) __epoll_pwait
android::Looper::pollInner(int)
android::Looper::pollOnce(int, int*, int*, void**)
android::android_os_MessageQueue_nativePollOnce(_JNIEnv*, _jobject*, long, int)
android.os.MessageQueue.nativePollOnce( MessageQueue.java:0 )
android.os.MessageQueue.next( MessageQueue.java:335 )
android.os.Looper.loop( Looper.java:193 )
android.app.ActivityThread.main( ActivityThread.java:8019 )
המלצה
פועלים לפי ההמלצות הכלליות למניעת מקרי ANR. לדוגמה, אפשר לזהות את המקומות בקוד שבהם הליבה הראשית של האפליקציה יכולה להיות עסוקה במשך יותר מ-5 שניות.