שגיאות מסוג 'האפליקציה לא מגיבה (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 שניות.