تحدث أخطاء "التطبيق لا يستجيب" (ANR) عندما لا تستجيب سلسلة واجهة المستخدم في التطبيق لأكثر من 5 ثوانٍ. يمكنك الاطّلاع على مزيد من المعلومات حول أخطاء ANR وتشخيصها في مستندات Android.
بالإضافة إلى ذلك، يمكن أن تساعدك أداة Crashlytics في تحديد سلاسل المحادثات التي تتضمّن مشاكل معيّنة. نحلّل أخطاء ANR، ثم في لوحة بيانات DevOps والتفاعل > Crashlytics، نضع علامات على سلاسل المحادثات ذات الصلة لتقديم تلميحات حول كيفية تصحيح أخطاء ANR.
توضّح الأقسام التالية في هذه الصفحة معنى كل علامة من علامات ANR، وتعرض
مثالاً على خطأ ANR يحمل هذه العلامة، وتقدّم حلاً مقترَحًا لتصحيح خطأ ANR.
Triggered ANR
يتم إضافة تعليق توضيحي إلى سلسلة التعليمات التي تم حظرها لفترة طويلة جدًا وأدّت إلى ظهور خطأ ANR باستخدام علامة Triggered ANR هذه.
يمكن أن تكون سلسلة المحادثات التي تتضمّن مشكلة هي سلسلة المحادثات الرئيسية للتطبيق أو أي سلسلة محادثات أخرى
تبيّن أنّها غير مستجيبة. ومع ذلك، قد يكون أو لا يكون سلسلة التعليمات التي تم وضع علامة Triggered ANR عليها هي السبب الفعلي لحدوث خطأ ANR. لتقديم إحصاءات حول تصحيح أخطاء ANR هذه وإصلاحها،
يضع Crashlytics أيضًا علامات على أي سلاسل محادثات أخرى مرتبطة بأخطاء ANR. في الأقسام التالية من هذه الصفحة، تعرَّف على العلامات الأخرى التي يمكن تطبيقها على سلسلة محادثات.
Deadlocked
يتم وضع علامة Deadlocked على أي سلاسل محادثات تبيّن أنّها متورطة في حالة توقّف تام أدت إلى حدوث خطأ ANR.
يحدث الجمود عندما يدخل مؤشر ترابط في حالة انتظار لأن المورد المطلوب يحتفظ به مؤشر ترابط آخر، والذي ينتظر أيضًا موردًا يحتفظ به مؤشر الترابط الأول. إذا كانت سلسلة التعليمات الرئيسية للتطبيق في هذه الحالة، من المحتمل حدوث أخطاء 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 prevention algorithms للتعرّف على الحلول الممكنة.
IO Root blocking
أي سلسلة تعليمات كانت تنفّذ عمليات بطيئة متعلقة بوحدات الإدخال والإخراج وحظرت سلسلة التعليمات 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 )
الاقتراح
بشكل عام، يجب ألا ينفّذ تطبيقك عمليات إدخال/إخراج مكلفة على سلسلة التعليمات الرئيسية. في حال كانت سلسلة التعليمات الرئيسية
IO Root blocking، يمكنك أيضًا استخدام
وضع التدقيق الصارم
لتحديد أي عمليات غير مقصودة متعلّقة بوحدات الإدخال والإخراج تحدث في سلسلة التعليمات
الرئيسية.
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 )
الاقتراح
قلِّل من العمل الذي يتطلّب استخدامًا مكثّفًا لوحدة المعالجة المركزية في سلسلة التعليمات الرئيسية. استخدِم سلاسل مهام العامل أو سلاسل المهام في الخلفية
لأداء المهام التي تستهلك وحدة المعالجة المركزية بشكل كبير.
قلِّل من العمليات التي تستهلك موارد وحدات الإدخال والإخراج بشكل كبير، مثل التحميل من قاعدة بيانات، في سلسلة التعليمات الرئيسية.
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 ثوانٍ.
Waiting for binder call
أي سلسلة محادثات كانت تنتظر مكالمة Binder وحظرت سلسلة المحادثات
Triggered ANR يتم إضافة تعليق توضيحي إليها باستخدام العلامة
Waiting for binder call. إذا لم يتم حظر سلسلة التعليمات
Triggered ANR من قِبل سلاسل تعليمات أخرى،
تكون سلسلة التعليمات Waiting for binder call أيضًا
سلسلة تعليمات Root blocking.
عرض مثال
Thread main(THREAD_STATE_NATIVE_WAITING)
syscall
android::IPCThreadState::talkWithDriver(bool)
android::IPCThreadState::waitForResponse(android::Parcel*, int*)
android::IPCThreadState::transact(int, unsigned int, android::Parcel const&, android::Parcel*, unsigned int)
android::BpBinder::transact(unsigned int, android::Parcel const&, android::Parcel*, unsigned int)
android_os_BinderProxy_transact(_JNIEnv*, _jobject*, int, _jobject*, _jobject*, int)
android.os.BinderProxy.transactNative( BinderProxy.java:0 )
android.os.BinderProxy.transact( BinderProxy.java:540 )
android.app.IActivityManager$Stub$Proxy.getContentProvider( IActivityManager.java:5945 )
android.app.ActivityThread.acquireProvider( ActivityThread.java:7138 )
android.app.ActivityThread$H.handleMessage( ActivityThread.java:1982 )
android.os.Handler.dispatchMessage( Handler.java:106 )
android.os.Looper.loop( Looper.java:236 )
android.app.ActivityThread.main( ActivityThread.java:8037 )
الاقتراح
بما أنّ وقت استجابة طلب Binder قد يكون غير متوقّع، يجب التعامل مع طلبات Binder
مثل العمليات المتعلقة بوحدات الإدخال والإخراج وتجنُّب إجرائها في سلسلة التعليمات الرئيسية إن أمكن. انقل الرمز الذي يؤدي إلى ظهور طلبات Binder (بما في ذلك طلبات بيانات من واجهة برمجة التطبيقات للنظام الأساسي التي تؤدي إلى معاملات Binder) إلى سلسلة تعليمات في الخلفية، أو خزِّن النتائج مؤقتًا إذا كان لا يمكن تجنُّب الطلب من سلسلة التعليمات الرئيسية.