Os ANRs do Unity acontecem por vários motivos. A maioria dos ANRs comuns é causada pelo uso indevido de componentes do Android e do Unity e pela falta de comunicação entre eles.
WebView
WebView
é uma classe do Android que mostra páginas da Web. SDKs de terceiros (como anúncios) usam WebView
para mostrar conteúdo da Web dinâmico
em atividades diferentes da UnityPlayerActivity
. Os ANRs ocorrem quando SDKs de terceiros
usam o WebView
de maneira inadequada.
Stack trace
O rastreamento de pilha é seu primeiro recurso para entender a causa do 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)
Figura 1.Rastreamento de pilha de ANR causado por uma espera de futex.
Causa
Até agora, a causa principal desse problema não está clara. Algumas possíveis causas incluem:
- Implementação inadequada de anúncios.
- Uma versão desatualizada do
WebView
, já que o usuário pode ter optado por não atualizar o app automaticamente. - Uso elevado de recursos do sistema (CPU, GPU etc.), o que pode exigir muito criação de perfil.
- Falhas na compilação de shader, que podem indicar que o
conteúdo tem um shader incompatível ou que o usuário tem uma versão antiga do
WebView
instalada.
Solução
- Para restringir o tipo de conteúdo que está fazendo com que o
WebView
bloqueie a thread principal, adicione registros ao seu jogo sempre que uma página da Web for carregada, exibida ou fechada.- É possível usar os serviços de relatórios Backtrace ou Crashlytics.
- Depois de analisar os dados e encontrar o problema, tente desativar os provedores de anúncios ofensivos.
- Inclua registros de memória para garantir que o problema não esteja relacionado à memória.
- Alerta o usuário para atualizar o
WebView
no Google Play. No Android 5.0 (nível 21 da API) e versões mais recentes, oWebView
foi movido para um APK. Portanto, ele pode ser atualizado separadamente da plataforma Android. Para saber qual versão doWebView
está em uso em um dispositivo, acesse Configurações > Apps > WebView do sistema Android e confira a versão na parte de baixo da página.

WebView
.Pausa do Unity
Quando UnityPlayerActivity
recebe uma chamada onPause()
, a seguinte cadeia de operações é iniciada:
UnityPlayerActivity
notifica o mecanismo de tempo de execução do Unity de que a atividade foi pausada.- O Unity chama todos os
MonoBehaviour
que implementam o eventoOnApplicationPause
. - O Unity interrompe os componentes e módulos, como reprodução de som, renderização, loop de jogo e animação.
- Para garantir que o
Unity Android Player
(UAP) e o mecanismo estejam sincronizados, o UAP aguarda 4 segundos para que o mecanismo pare. - Se essa operação levar mais de 5 segundos, o sistema vai acionar um ANR.
Stack trace
"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)
Figura 3. ANR causado por um semáforo que nunca é liberado.
Solução
Verifique se o código do jogo em C# não leva muito tempo para concluir a execução durante um evento de pausa ou retomada.
- Crie um perfil do jogo e verifique se o
OnApplicationPause
é uma operação cara. Você pode usar umStopwatch
. - Evite operações de E/S ou solicitações de rede síncronas.
- Mova as operações para outro
Thread
usando oTask
. O Unity 2023.1 é compatível com um modelo de programação assíncrona simplificado usando as palavras-chaveasync
eawait
do C#.
UnitySendMessage bloqueado
Os plug-ins e SDKs do Java Unity enviam dados para a camada de jogo C# usando o JNI. No entanto, essa comunicação pode bloquear a linha de execução principal devido a uma rotina de sincronização nativa, como um mutex, causando um ANR devido à disputa de bloqueio.
Stack trace
O ANR na Figura 4 foi causado por uma operação longa no código C# chamada por um plug-in Java. O mecanismo Unity usa um mutex de herança de não prioridade para garantir a execução correta.
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)
Figura 4. ANR causado por uma disputa de bloqueio.
Causa
O problema é que várias mensagens são enviadas quando o aplicativo é retomado. As mensagens ficam na fila porque não podem ser enviadas enquanto o jogo está em segundo plano. As mensagens são enviadas simultaneamente quando o app é retomado.
Durante um período de pausa, geralmente armazenamos as informações do jogo no servidor. Por exemplo, registramos a posição de um jogador no jogo para que ele possa voltar ao mesmo lugar quando o jogo for retomado.
Essa carga de trabalho, combinada com outro código de terceiros que cria a própria carga de trabalho, pode sobrecarregar os recursos do dispositivo, principalmente a linha de execução principal. A thread principal executa a interface do usuário de um app e geralmente é o principal local de ANRs. Portanto, qualquer carga de trabalho adicionada na linha de execução principal aumenta o potencial de um ANR.
Solução
Durante uma pausa do aplicativo, verifique se todas as ações de código são necessárias ou tente salvar o estado do usuário na memória do dispositivo local. E, claro, verifique se você também pode concluir essas ações fora do período de pausa.
Algumas abordagens:
- Mova a operação C# que processa uma mensagem para uma linha de execução
diferente da principal.
- Se o código não depender do contexto da linha de execução principal do Unity, use
Task
para comunicação em vez de mensagem.
- Se o código não depender do contexto da linha de execução principal do Unity, use
- Não envie várias mensagens do plug-in quando o jogo estiver pausado.
- O mecanismo não pode enviar mensagens enquanto o jogo está em segundo plano.
- Envie apenas o último estado de dados para o jogo se isso não afetar a funcionalidade dele.
Install Referrer
O referenciador de instalação do Google Play é uma string exclusiva enviada à Play Store sempre que um usuário clica em um anúncio. É um identificador de rastreamento de anúncios específico do Android. Depois de instalado, o app envia o referenciador de instalação ao parceiro de atribuição, que corresponde à origem com a instalação (atribuindo a conversão).
Stack trace
A Figura 5 mostra um rastreamento de pilha de ANR de um jogo que usa o SDK do Facebook para recuperar a atribuição de instalação.

Causa
O ANR foi causado por uma chamada de vinculação lenta. No entanto, não é possível determinar a causa principal sem acesso ao código-fonte do SDK.
Solução
Para resolver esse tipo de problema, é necessário entrar em contato com o desenvolvedor do SDK ou fazer muitas pesquisas on-line para encontrar uma solução em potencial, verificar se uma versão mais recente do SDK resolve o ANR para outras pessoas ou até mesmo testar uma pequena estratégia de lançamento.
O Google oferece uma página do SDK Index que combina dados de uso de apps do Google Play com informações coletadas pela detecção de código para especificar atributos e indicadores que ajudam você a decidir se quer adotar, manter ou remover um SDK do app.
Outros recursos
Para saber mais sobre ANRs, consulte os seguintes recursos:
- Depurar ANRs: desenvolvimento de jogos para Android
- ANRs: qualidade do app