Błędy ANR w Unity mogą występować z różnych powodów. Najczęstsze błędy ANR są spowodowane nieprawidłowym użyciem komponentów Androida i Unity oraz ich nieprawidłową komunikacją.
WebView | komponent WebView
WebView
to klasa Androida, która wyświetla strony internetowe. Pakiety SDK innych firm (np. do reklam) używają WebView
do wyświetlania dynamicznych treści internetowych w aktywnościach innych niż UnityPlayerActivity
. Błędy ANR występują, gdy zewnętrzne pakiety SDK nieprawidłowo używają WebView
.
Zrzut stosu
Ślad stosu to pierwsze miejsce, w którym możesz szukać przyczyny błędu 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)
Ilustracja 1. Zrzut stosu błędu ANR spowodowanego oczekiwaniem na futex.
Przyczyna
Główna przyczyna tego problemu jest na razie nieznana. Oto niektóre możliwe przyczyny:
- Nieprawidłowe wdrożenie reklamy.
- nieaktualną wersję
WebView
, ponieważ użytkownik mógł zrezygnować z automatycznego aktualizowania aplikacji; - Wysokie wykorzystanie zasobów systemowych (procesora, GPU itp.), które może wymagać wielu profili.
- Błędy kompilacji shadera, które mogą wskazywać, że treść zawiera niezgodny shader lub że użytkownik ma zainstalowaną starą wersję
WebView
.
Rozwiązanie
- Aby zawęzić zakres typów treści, które powodują blokowanie wątku głównego przez
WebView
, dodaj do gry logi za każdym razem, gdy strona internetowa jest wczytywana, wyświetlana lub zamykana.- Możesz używać usług raportowania Backtrace lub Crashlytics.
- Następnie po przeanalizowaniu danych i znalezieniu problemu spróbuj wyłączyć dostawców reklam, którzy go powodują.
- Dołącz dzienniki pamięci, aby mieć pewność, że problem nie jest związany z pamięcią.
- Poinformuj użytkownika, że musi zaktualizować
WebView
w Google Play. Od Androida 5.0 (poziom interfejsu API 21) i nowszychWebView
zostało przeniesione do pliku APK. Dlatego można ją aktualizować niezależnie od platformy Android. Aby sprawdzić, która wersjaWebView
jest używana na urządzeniu, otwórz Ustawienia > Aplikacje > System Android WebView i sprawdź wersję u dołu strony.

WebView
.Wstrzymanie Unity
Gdy UnityPlayerActivity
odbierze połączenie onPause()
, rozpoczyna się następujący ciąg operacji:
UnityPlayerActivity
informuje środowisko wykonawcze Unity, że aktywność została wstrzymana.- Unity wywołuje każdy obiekt
MonoBehaviour
, który implementuje zdarzenieOnApplicationPause
. - Unity zatrzymuje swoje komponenty i moduły, takie jak odtwarzanie dźwięku, renderowanie, pętla gry i animacja.
- Aby mieć pewność, że zarówno
Unity Android Player
(UAP), jak i silnik są zsynchronizowane, UAP czeka 4 sekundy, aż silnik się zatrzyma. - Jeśli ta operacja trwa dłużej niż 5 sekund, system wywołuje błąd ANR.
Zrzut stosu
"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)
Rysunek 3. Błąd ANR spowodowany przez semafor, który nigdy nie został zwolniony.
Rozwiązanie
Upewnij się, że kod gry w C# nie wykonuje się zbyt długo podczas zdarzenia wstrzymania lub wznowienia.
- Sprawdź profil swojej gry i zobacz, czy
OnApplicationPause
jest kosztowną operacją. Możesz użyćStopwatch
. - Unikaj operacji wejścia/wyjścia i synchronicznych żądań sieciowych.
- Przenieś operacje do innego
Thread
za pomocą ikonyTask
. Unity 2023.1 obsługuje uproszczony model programowania asynchronicznego z użyciem słów kluczowych C#async
iawait
.
Funkcja UnitySendMessage zablokowana
Wtyczki i pakiety SDK Java Unity wysyłają dane do warstwy gry C# za pomocą JNI. Jednak ta komunikacja może blokować wątek główny z powodu procedury synchronizacji natywnej, takiej jak muteks, co powoduje błąd ANR z powodu konfliktu blokad.
Zrzut stosu
Błąd ANR na rysunku 4 został spowodowany przez długą operację w kodzie C# wywoływanym przez wtyczkę Java. Silnik Unity używa muteksu dziedziczenia bez priorytetu, aby zapewnić prawidłowe wykonanie.
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)
Rysunek 4. Błąd ANR spowodowany przez rywalizację o blokadę.
Przyczyna
Problem polega na tym, że po wznowieniu działania aplikacji wysyłanych jest kilka wiadomości. Wiadomości są umieszczane w kolejce, ponieważ nie można ich wysłać, gdy gra jest w tle. Wszystkie wiadomości są wysyłane jednocześnie po wznowieniu działania aplikacji.
Podczas przerwy zwykle przechowujesz informacje o grze na serwerze. Na przykład zapisujesz pozycję gracza w grze, aby mógł on wrócić do tego samego miejsca po wznowieniu rozgrywki.
To obciążenie w połączeniu z innym kodem zewnętrznym, który tworzy własne obciążenie, może przeciążyć zasoby urządzenia, zwłaszcza wątek główny. Główny wątek obsługuje interfejs użytkownika aplikacji i jest często głównym źródłem błędów ANR. Dlatego każde dodatkowe obciążenie wątku głównego zwiększa ryzyko wystąpienia błędu ANR.
Rozwiązanie
Podczas wstrzymania aplikacji upewnij się, że wszystkie działania kodu są niezbędne, lub spróbuj zapisać stan użytkownika w pamięci lokalnej urządzenia. Sprawdź też, czy możesz wykonać te działania poza okresem wstrzymania.
Kilka podejść:
- Przenieś operację w języku C#, która obsługuje wiadomość, do wątku innego niż wątek główny.
- Jeśli Twój kod nie zależy od kontekstu głównego wątku Unity, do komunikacji używaj
Task
zamiast wiadomości.
- Jeśli Twój kod nie zależy od kontekstu głównego wątku Unity, do komunikacji używaj
- Nie wysyłaj wielu wiadomości z wtyczki, gdy gra jest wstrzymana.
- Gdy gra działa w tle, silnik nie może wysyłać wiadomości.
- Wysyłaj do gry tylko ostatni stan danych, jeśli nie ma to wpływu na jej działanie.
Strona odsyłająca instalację
Play Install Referrer to unikalny ciąg znaków wysyłany do Sklepu Play za każdym razem, gdy użytkownik kliknie reklamę. Jest to identyfikator śledzenia reklam na Androidzie. Po zainstalowaniu aplikacja wysyła do partnera atrybucji informację o źródle instalacji, która jest dopasowywana do instalacji (przypisanie konwersji).
Zrzut stosu
Ilustracja 5 przedstawia ślad stosu błędu ANR z gry, która używa pakietu SDK Facebooka do pobierania atrybucji instalacji.

Przyczyna
Błąd ANR został spowodowany przez powolne wywołanie Binder. Nie można jednak określić głównej przyczyny bez dostępu do kodu źródłowego pakietu SDK.
Rozwiązanie
Rozwiązanie tego typu problemu wymaga kontaktu z deweloperem pakietu SDK lub intensywnego wyszukiwania w internecie potencjalnego rozwiązania, sprawdzania, czy nowsza wersja pakietu SDK rozwiązuje problem z błędem ANR u innych użytkowników, a nawet eksperymentowania z strategią stopniowego wdrażania.
Google udostępnia stronę SDK Index, która łączy dane o wykorzystaniu pochodzące z aplikacji w Google Play oraz informacje zebrane za pomocą wykrywania kodu. Tworzy w ten sposób atrybuty i sygnały ułatwiające podjęcie decyzji, czy warto wdrożyć pakiet SDK, zachować go czy usunąć z aplikacji.
Dodatkowe materiały
Więcej informacji o błędach ANR znajdziesz w tych materiałach:
- Debugowanie błędów ANR – tworzenie gier na Androida
- Błędy ANR – jakość aplikacji