أخطاء ANR الشائعة في ألعاب Unity

تحدث أخطاء ANR في Unity لأسباب مختلفة. تنتج معظم أخطاء ANR الشائعة عن إساءة استخدام مكوّنات Android وUnity وسوء التواصل بينهما.

WebView

WebView هي فئة من فئات Android تعرض صفحات الويب. جهة خارجية تستخدم حِزم تطوير البرامج (SDK) (مثل الإعلانات) WebView لعرض محتوى الويب الديناميكي. في أنشطة أخرى غير UnityPlayerActivity. تحدث أخطاء ANR عندما تسيء حِزم SDK استخدام "WebView".

تتبع التكديس

يُعد تتبُّع تسلسل استدعاء الدوال البرمجية أول طريق لفهم سبب خطأ ANR.

/data/app/~~p-0ksfCD6bF6Sdq6kpVePg==/com.google.android.webview-5YQZOqKbbqp-uoLY6WYnTw==/base.apk!libmonochrome.so
  at J.N.Mhc_M_H$ (Native method)
  at org.chromium.components.viz.service.frame_sinks.ExternalBeginFrameSourceAndroid.doFrame (chromium-TrichromeWebViewGoogle.aab-stable-579013831:60)
  at android.view.Choreographer$CallbackRecord.run (Choreographer.java:1054)
  at android.view.Choreographer.doCallbacks (Choreographer.java:878)
  at android.view.Choreographer.doFrame (Choreographer.java:807)
  at android.view.Choreographer$FrameDisplayEventReceiver.run (Choreographer.java:1041)
  at android.os.Handler.handleCallback (Handler.java:938)
  at android.os.Handler.dispatchMessage (Handler.java:99)
  at android.os.Looper.loop (Looper.java:223)
  at android.app.ActivityThread.main (ActivityThread.java:7721)
  at java.lang.reflect.Method.invoke (Native method)
  at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:592)
  at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:952)

الشكل 1.تتبُّع تسلسل استدعاء الدوال البرمجية لأخطاء ANR بسبب انتظار فوتكس

السبب

حتى الآن، السبب الأساسي لهذه المشكلة غير واضح. قد تتضمن بعض الأسباب المحتملة تشمل:

  • تنفيذ الإعلان سيئ.
  • إصدار قديم من WebView لأنّ المستخدم ربما اختار عدم التحديث. التطبيق تلقائيًا.
  • استهلاك مرتفع لموارد النظام (وحدة المعالجة المركزية (CPU) أو وحدة معالجة الرسومات أو غير ذلك)، وقد يتطلّب ذلك تحديد المواصفات الشخصية.
  • تتعطل تجميع Shader، مما قد يشير إلى أن المحتوى يتضمّن أداة تظليل غير متوافقة أو أنّ المستخدم لديه WebView قديم تم تثبيت نسخة منه.

الحل

  • لتضييق نطاق نوع المحتوى الذي يتسبب في حظر WebView وأضف السجلات إلى لعبتك كلما تم تحميل صفحة ويب أو عرضها أو مغلق.
    • يمكنك استخدام Backtrace أو Crashlytics. وخدمات الإبلاغ عن المحتوى.
    • ثم بعد تحليل البيانات وإيجاد المشكلة، حاول تعطيل موفّري الإعلانات المسيءين
    • يمكنك تضمين سجلّات الذاكرة للتأكّد من أنّ المشكلة لا تتعلّق بالذاكرة.
  • أرسِل تنبيهًا إلى المستخدم لتحديث WebView من Google Play. بدءًا من الإصدار Android 5.0 (المستوى 21 من واجهة برمجة التطبيقات) والإصدارات الأحدث، تم نقل WebView إلى حزمة APK. لذلك، من الممكن كل إصدار بشكل منفصل عن نظام Android الأساسي. لمعرفة إصدار WebView قيد الاستخدام على جهاز، انتقل إلى الإعدادات > التطبيقات > نظام Android WebView والاطّلاع على الإصدار في أسفل الصفحة.
شاشة معلومات التطبيق تعرض إصدارات WebView
الشكل 1. التحقّق من إصدار WebView

إيقاف Unity مؤقتًا

عندما يتلقى UnityPlayerActivity اتصال onPause()، تتوفر سلسلة بدء العمليات:

  1. يُرسِل "UnityPlayerActivity" إشعارًا إلى محرك بيئة تشغيل Unity بأنّ النشاط يحتوي على متوقف مؤقتًا.
  2. يستدعي الانسجام كل MonoBehaviour ينفذ الحدث OnApplicationPause.
  3. يوقف Unity عناصره ووحداته، مثل تشغيل الصوت والعرض وحلقة الألعاب والرسوم المتحركة.
  4. للتأكّد من أنّ كلاً من Unity Android Player (UAP) ومحرّك البحث تتم مزامنتها، ينتظر UAP 4 ثوانٍ حتى يتوقف المحرك.
  5. إذا استغرقت هذه العملية أكثر من 5 ثوانٍ، يعرض النظام خطأ ANR.

تتبع التكديس

"main" tid=1 Timed Waiting
jdk.internal.misc.Unsafe.park (Native method)
java.util.concurrent.locks.LockSupport.parkNanos (LockSupport.java:234)
java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireSharedNanos (AbstractQueuedSynchronizer.java:1079)
java.util.concurrent.locks.AbstractQueuedSynchronizer.tryAcquireSharedNanos (AbstractQueuedSynchronizer.java:1369)
java.util.concurrent.Semaphore.tryAcquire (Semaphore.java:415)
com.unity3d.player.UnityPlayer.pauseUnity (UnityPlayer.java:833)
com.unity3d.player.UnityPlayer.pause (UnityPlayer.java:796)
com.unity3d.player.UnityPlayerActivity.onPause (UnityPlayerActivity.java:117)
android.app.Activity.performPause (Activity.java:8517)
android.app.Instrumentation.callActivityOnPause (Instrumentation.java:1618)
android.app.ActivityThread.performPauseActivityIfNeeded (ActivityThread.java:5061)
android.app.ActivityThread.performPauseActivity (ActivityThread.java:5022)
android.app.ActivityThread.handlePauseActivity (ActivityThread.java:4974)
android.app.servertransaction.PauseActivityItem.execute (PauseActivityItem.java:48)
android.app.servertransaction.ActivityTransactionItem.execute (ActivityTransactionItem.java:45)
android.app.servertransaction.TransactionExecutor.executeLifecycleState (TransactionExecutor.java:179)
android.app.servertransaction.TransactionExecutor.execute (TransactionExecutor.java:97)
android.app.ActivityThread$H.handleMessage (ActivityThread.java:2303)
android.os.Handler.dispatchMessage (Handler.java:106)
android.os.Looper.loopOnce (Looper.java:201)
android.os.Looper.loop (Looper.java:288)
android.app.ActivityThread.main (ActivityThread.java:7884)
java.lang.reflect.Method.invoke (Native method)
com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:548)
com.android.internal.os.ZygoteInit.main (ZygoteInit.java:936)

الشكل 3. خطأ ANR ناتج عن إشارة لم يتم إصدارها أبدًا.

الحل

تأكد من أن رمز لعبة C# لا يستغرق وقتًا طويلاً جدًا لإنهاء التنفيذ أثناء أو إيقاف الحدث مؤقتًا أو استئنافه.

  • أنشِئ ملفًّا شخصيًا للعبتك وتحقَّق مما إذا كان OnApplicationPause تكلفة عالية العملية. يمكنك استخدام Stopwatch.
  • تجنَّب عمليات وحدات الإدخال والإخراج (I/O) أو طلبات الشبكة المتزامنة.
  • نقل العمليات إلى Thread آخر باستخدام Task يوفّر Unity 2023.1 بيئة مبسّطة نموذج البرمجة غير المتزامن باستخدام C# الكلمات الرئيسية async وawait.

تم حظر UnitySendMessage.

تُرسِل المكوّنات الإضافية وحِزم تطوير البرامج (SDK) في Java Unity البيانات إلى طبقة الألعاب C# باستخدام JNI. ومع ذلك، قد تحظر هذه الرسالة سلسلة التعليمات الرئيسية بسبب وجود سلسلة إجراءات المزامنة، مثل دالة الاستبعاد المتبادل، التي تتسبّب في حدوث خطأ ANR بسبب تزايد الطلب على دالة الاستبعاد المتبادل.

تتبع التكديس

حدث خطأ ANR في الشكل 4 بسبب عملية طويلة في الرمز C# تم استدعائها بواسطة المكون الإضافي لـ Java. يستخدم محرّك Unity طريقة اكتساب غير ذات أولوية دالة الاستبعاد المتبادل لضمان التنفيذ الصحيح.

libc.so NonPI::MutexLockWithTimeout(pthread_mutex_internal_t*, bool, timespec const*) + 604
com.unity3d.player.UnityPlayer.nativeUnitySendMessage (Native method)
com.unity3d.player.UnityPlayer.UnitySendMessage (UnityPlayer.java:665)

الشكل 4 خطأ ANR ناتج عن تعارض في القفل.

السبب

تكمن المشكلة في أنه يتم إرسال العديد من الرسائل عندما يكون التطبيق . تمت إضافة الرسائل إلى قائمة الانتظار لأنّه لا يمكن إرسالها أثناء اللعبة. تعمل في الخلفية. يتم إرسال جميع الرسائل بشكل متزامن عند يستأنف التطبيق.

خلال فترة الإيقاف المؤقت، يتم بشكل عام تخزين معلومات لعبتك على الخادم، على سبيل المثال، يمكنك تسجيل موضع اللاعب في اللعبة حتى يتمكن يمكنه العودة إلى المكان نفسه عند استئناف اللعبة.

عبء العمل هذا، إلى جانب رمز برمجي آخر تابع لجهة خارجية ينشئ عبء العمل الخاص به، قد يؤدي إلى زيادة الحمل على موارد الجهاز، لا سيما سلسلة التعليمات الرئيسية. المصدر الرئيسي تعمل سلسلة التعليمات على تشغيل واجهة مستخدم التطبيق وغالبًا ما تكون الموقع الرئيسي لأخطاء ANR. وبالتالي، يزيد أي أعباء عمل إضافية في سلسلة التعليمات الرئيسية من احتمال حدوث خطأ ANR.

الحل

أثناء إيقاف التطبيق مؤقتًا، تأكّد من أنّ جميع إجراءات الرمز ضرورية، أو جرِّب حفظ حالة المستخدم في ذاكرة جهازك المحلية. وبالطبع، يمكنك معرفة ما إذا كان بإمكانك أيضًا إكمال هذه الإجراءات خارج فترة الإيقاف المؤقت.

بعض الأساليب:

  • نقل عملية C# التي تعالج رسالة إلى سلسلة محادثات بخلاف سلسلة التعليمات الرئيسية.
    • إذا كانت الرمز البرمجي لا يعتمد على سياق سلسلة التعليمات الرئيسية في Unity، استخدِم Task للتواصل بدلاً من الرسالة.
  • لا ترسل رسائل متعددة من المكون الإضافي عند إيقاف اللعبة مؤقتًا.
    • لا يمكن للمحرّك إرسال رسائل عندما تعمل اللعبة في الخلفية.
    • يجب عدم إرسال حالة البيانات الأخيرة إلى لعبتك إلّا إذا لم يؤثّر ذلك في اللعبة. الأخرى.

تثبيت المُحيل

مُحيل التثبيت على Play هو سلسلة فريدة يتم إرسالها إلى "متجر Play" عند ينقر مستخدم على إعلان. وهو معرّف تتبُّع الإعلان الخاص بنظام التشغيل Android. مرة واحدة مثبّتًا، يرسل التطبيق مُحيل التثبيت إلى شريك الإحالة، تطابق المصدر مع عملية التثبيت (مع نَسب الإحالة الناجحة).

تتبع التكديس

يعرض الشكل 5 تتبُّع تسلسُل استدعاء الدوال البرمجية لأخطاء ANR من لعبة تستخدم حِزمة تطوير البرامج (SDK) الخاصة بتطبيق Facebook من أجل استرداد إحالة عمليات التثبيت.

الشكل 5. يحتوي تقرير "مؤشرات Android الحيوية" على طلب Binder.

السبب

حدث خطأ ANR بسبب بطء طلب الربط. ومع ذلك، لا يمكن تحديد السبب تم تحديدها بدون الوصول إلى رمز مصدر حزمة تطوير البرامج (SDK).

الحل

يتضمن حل هذا النوع من المشكلات التواصل مع مطوّر SDK أو الكثير من البحث عبر الإنترنت عن حل محتمل، والتحقق مما إذا كان حل من حزمة تطوير البرامج (SDK) لحل أخطاء ANR لدى المستخدمين الآخرين، أو حتى تجربة أداة استراتيجية طرح التطبيقات.

يوفّر Google صفحة أداة SDK Index التي تجمع بين بيانات الاستخدام. من تطبيقات Google Play التي تتضمّن معلومات يتم جمعها من خلال رصد الرموز البرمجية وتوفر سمات وإشارات مصممة لمساعدتك في اتخاذ قرار بشأن اعتماد أو الاحتفاظ بحزمة SDK أو إزالتها من تطبيقك

مصادر إضافية

لمزيد من المعلومات عن أخطاء ANR، يمكنك الاطّلاع على المراجع التالية: