Błędy ANR w Unity występują z różnych powodów. Najczęstsze błędy ANR są spowodowane niewłaściwego użycia Androida i komponentów Unity oraz nieporozumień.
WebView | komponent WebView
WebView
to klasa na Androida, która wyświetla strony internetowe. Dostarczane przez inną firmę
Pakiety SDK (np. reklamy) używają WebView
do wyświetlania dynamicznych treści internetowych
w ramach aktywności innych niż UnityPlayerActivity
. Błędy ANR występują, gdy
Pakiety SDK nieprawidłowo używają WebView
.
Zrzut stosu
Zrzut stosu to pierwszy sposób na zrozumienie 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)
Rys. 1. Zrzut stosu ANR spowodowany przez oczekiwanie futex.
Przyczyna
Jak dotąd główna przyczyna tego problemu nie jest znana. Niektóre potencjalne przyczyny uwzględnij:
- Błędna implementacja reklamy.
- Nieaktualna wersja
WebView
, ponieważ użytkownik mógł zrezygnować z aktualizacji automatycznie uruchamiać aplikację. - Duże wykorzystanie zasobów systemowych (CPU, GPU itp.), które może wymagać dużo pracy profilowanie.
- Awaria kompilacji Shader, co może oznaczać, że
treść ma niezgodny program do cieniowania lub użytkownik korzysta ze starszej wersji
WebView
.
Rozwiązanie
- Aby doprecyzować, jaki typ treści sprawia, że
WebView
blokuje w wątku głównym, dodawaj logi do gry za każdym razem, gdy wczytuje się, wyświetla lub zamknięte.- Możesz używać aplikacji Backtrace lub Crashlytics i usług raportowych.
- Następnie, po przeanalizowaniu danych i znalezieniu problemu, spróbuj wyłączyć tych obraźliwych.
- Dołącz dzienniki pamięci, aby mieć pewność, że problem nie jest związany z pamięcią.
- Poproś użytkownika o zaktualizowanie aplikacji
WebView
z Google Play. Android 5.0 (poziom interfejsu API 21) lub wyższym, aplikacjaWebView
została przeniesiona do pliku APK. W związku z tym aktualizowane niezależnie od platformy Androida. Aby sprawdzić wersję aplikacjiWebView
jest używany na urządzeniu, wybierz Ustawienia > Aplikacje > System Android WebView i sprawdź wersję u dołu strony.
Pauza
Gdy UnityPlayerActivity
otrzymuje wywołanie onPause()
, następujący łańcuch:
operacje rozpoczynają się:
UnityPlayerActivity
powiadamia silnik środowiska wykonawczego Unity, że aktywność wstrzymane.- Unity wywołuje każde
MonoBehaviour
, które implementuje metodęOnApplicationPause
. - Unity zatrzymuje komponenty i moduły, takie jak odtwarzanie dźwięku, renderowanie pętla gry i animacja.
- Aby upewnić się, że
Unity Android Player
(UAP) i wyszukiwarka są zsynchronizowane, UAP czeka 4 sekundy na zatrzymanie silnika. - Jeśli ta operacja potrwa dłużej niż 5 sekund, system wywoła 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ł wydany.
Rozwiązanie
Upewnij się, że wykonanie kodu gry w C# nie trwa zbyt długo wstrzymać lub wznowić wydarzenie.
- Profiluj grę i sprawdź, czy
OnApplicationPause
nie jest drogi w drodze . Możesz użyć:Stopwatch
. - Unikaj operacji wejścia-wyjścia i synchronicznych żądań sieciowych.
- Przenieś operacje do innego zasobu
Thread
za pomocąTask
Unity 2023.1 obsługuje uproszczone asynchroniczny model programowania w C#async
iawait
słów kluczowych.
Zablokowano UnitySendMessage
Wtyczki Java Unity i pakiety SDK wysyłają dane do warstwy gry C# za pomocą JNI. Jednak ta komunikacja może zablokować główny wątek ze względu na procedury synchronizacji, takiej jak mutex, powodując błąd ANR z powodu rywalizacji o blokadę.
Zrzut stosu
Błąd ANR na ilustracji 4 został spowodowany długą operację w kodzie C# wywołaną przez Wtyczka Java. Mechanizm Unity wykorzystuje dziedziczenie inne niż priorytetowe mutex, 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 rywalizacją o blokadę.
Przyczyna
Problem polega na tym, że gdy aplikacja jest wysyłanych kilka wiadomości, został wznowiony. Wiadomości znajdują się w kolejce, ponieważ nie można ich wysłać w czasie gry Aplikacja znajduje się w tle. Wszystkie wiadomości są wysyłane jednocześnie, gdy wznowienie aplikacji.
W tym czasie zwykle przechowujesz informacje o grze na serwer; Na przykład możesz rejestrować pozycję gracza w grze, mogą wrócić do tego samego miejsca po wznowieniu gry.
To zadanie, w połączeniu z innym kodem innej firmy tworzącym własny zbiór zadań, może przeciążyć zasoby urządzenia, zwłaszcza wątek główny. Główny uruchamia interfejs użytkownika aplikacji i często jest główną lokalizacją błędów ANR. A więc, każde dodane zadanie w wątku głównym zwiększa ryzyko błędu ANR.
Rozwiązanie
Podczas wstrzymania aplikacji sprawdź, czy wszystkie działania związane z kodem są konieczne. spróbuj zapisać stan użytkownika w lokalnej pamięci urządzenia. Oczywiście zobaczysz też czy możesz wykonać te działania również poza okresem wstrzymania.
Jak zrobić to na kilka sposobów:
- Przenieś operację C#, która obsługuje wiadomość, do wątku
niż w głównym wątku.
- Jeśli Twój kod nie zależy od głównego wątku Unity, użyj
Task
na potrzeby komunikacji zamiast wysyłania wiadomości.
- Jeśli Twój kod nie zależy od głównego wątku Unity, użyj
- Nie wysyłaj wielu wiadomości z wtyczki, gdy gra jest wstrzymana.
- Silnik nie może wysyłać wiadomości, gdy gra działa w tle.
- Wysyłaj ostatni stan danych do gry tylko wtedy, gdy nie ma to wpływu na grę funkcji.
Zainstaluj stronę odsyłającą
Strona odsyłająca do instalacji z Play to unikalny ciąg znaków wysyłany do Sklepu Play, gdy użytkownik klika reklamę. Jest to identyfikator śledzenia reklam specyficzny dla Androida. Jednorazowo aplikacja wysyła stronę odsyłającą do instalacji do partnera atrybucji, który dopasowuje źródło do instalacji (przypisuje konwersję).
Zrzut stosu
Rysunek 5 przedstawia zrzut stosu ANR z gry, która korzysta z pakietu SDK Facebooka, pobierze dane o atrybucji instalacji.
Przyczyna
Błąd ANR został spowodowany przez powolne wywołanie Binder. Nie możemy jednak określić bez dostępu do kodu źródłowego pakietu SDK.
Rozwiązanie
Rozwiązanie tego typu problemów wymaga komunikacji z deweloperem pakietu SDK lub często szukają w internecie potencjalnego rozwiązania, sprawdzając, czy nowszy model pozwala na rozwiązanie problemu ANR u innych. Czas eksperymentu jest też strategii wdrożenia.
Google udostępnia stronę indeksu SDK, która łączy dane o korzystaniu od aplikacji Google Play z informacjami zbieranymi za pomocą wykrywania kodu, dostarczają atrybuty i sygnały, które mają pomóc w podjęciu decyzji, zachować lub usunąć pakiet SDK 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.