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