Os ANRs do Unity ocorrem por vários motivos. Os ANRs mais comuns são causados por: uso indevido dos componentes do Android e do Unity e a falha de comunicação deles.
WebView
WebView
é uma classe do Android que mostra páginas da Web. Terceiros
Os SDKs (como anúncios) usam WebView
para mostrar conteúdo dinâmico da Web.
em atividades diferentes da UnityPlayerActivity
. ANRs ocorrem quando terceiros
Os SDKs usam WebView
de maneira indevida.
Stack trace
O stack trace é 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.stack trace de ANR causado por uma espera futex.
Causa
Até o momento, a causa raiz desse problema não está clara. Algumas possíveis causas podem incluem:
- Implementação de anúncio incorreta.
- 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 perfis.
- A compilação de sombreador falha, o que pode indicar que o
o conteúdo tem um sombreador incompatível ou o usuário tem uma
WebView
antiga. instalada.
Solução
- Para restringir o tipo de conteúdo que está fazendo com que a
WebView
bloqueie o linha de execução principal, adicione registros ao jogo sempre que uma página da Web for carregada, exibida ou fechados.- É possível usar o Backtrace ou o Crashlytics de relatórios do Google Cloud.
- Depois de analisar os dados e encontrar o problema, tente desativar o os provedores de anúncios ofensivos.
- Inclua registros de memória para garantir que o problema não esteja relacionado à memória.
- Alerte o usuário para atualizar o
WebView
no Google Play. Do Android 5.0 (nível 21 da API) e versões mais recentes, o appWebView
foi movido para um APK. Portanto, pode ser atualizados separadamente da plataforma Android. Para saber qual versão doWebView
está em uso em um dispositivo, acesse Configurações > Aplicativos > Sistema Android WebView e analise a versão na parte de baixo da página.
Pausa do Unity
Quando UnityPlayerActivity
recebe uma chamada onPause()
, a seguinte cadeia de
operações começa:
UnityPlayerActivity
notifica o mecanismo de tempo de execução do Unity que a atividade tem pausado.- O Unity chama cada
MonoBehaviour
que implementa oOnApplicationPause
. - 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 forem sincronizados, o UAP esperará quatro segundos para que o mecanismo seja interrompido. - Se essa operação levar mais de cinco 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 sinal semáforo que nunca é lançado.
Solução
Certifique-se de que o código do jogo em C# não demore muito para terminar a execução durante uma pausar ou retomar o evento.
- Crie um perfil do jogo e confira se o
OnApplicationPause
é caro operação 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 oferece suporte a uma modelo de programação assíncrona usando C#async
eawait
palavras-chave.
UnitySendMessage bloqueado
Os plug-ins e os SDKs do Java para Unity enviam dados para a camada do jogo em C# usando a JNI (link em inglês). No entanto, essa comunicação pode bloquear a linha de execução principal devido a um rotina de sincronização, como um mutex, causando um ANR devido à contenção 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 do Java. O mecanismo do Unity usa uma herança sem prioridade mutex 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 contenção de bloqueio.
Causa
O problema é que várias mensagens são enviadas quando o aplicativo for retomado. As mensagens estão na fila porque não podem ser enviadas durante o jogo fica em segundo plano. As mensagens são todas despachadas simultaneamente quando o app é retomado.
Durante um período de pausa, você geralmente armazena as informações do jogo na servidor por exemplo, você registra a posição de um jogador no jogo para que ele podem voltar para o mesmo lugar quando o jogo for retomado.
Essa carga de trabalho, combinada com outro código de terceiros que cria a própria, pode sobrecarregar os recursos do dispositivo, especialmente a linha de execução principal. O principal linha de execução executa a interface do usuário de um app e costuma ser o principal local dos ANRs. Então, qualquer carga de trabalho adicionada à linha de execução principal aumenta o potencial de um ANR.
Solução
durante uma pausa de aplicativo, verifique se todas as ações no código são necessárias; ou tente salvar o estado do usuário na memória local do dispositivo. E, claro, ver se você também pode concluir essas ações fora do período de pausa.
Algumas abordagens:
- Mover a operação do C# que processa uma mensagem para uma linha de execução
diferente da linha de execução principal.
- Se o código não depender do contexto principal da linha de execução do Unity, use
Task
para comunicação em vez de mensagem.
- Se o código não depender do contexto principal da linha de execução 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.
- Só envie o último estado dos dados para seu jogo se isso não o afetar funcionalidade de armazenamento.
Instalar o referenciador
Referenciador de instalação do Google Play é uma string exclusiva enviada à Play Store sempre que um o usuário clica em um anúncio. É um identificador de acompanhamento de anúncios específico para Android. Uma vez instalado, o app envia o referenciador de instalação para o parceiro de atribuição, que corresponde a origem à instalação (atribuindo a conversão).
Stack trace
A Figura 5 mostra um stack trace 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, a causa raiz não pode ser é determinado sem acesso ao código-fonte do SDK.
Solução
A solução desse tipo de problema envolve a comunicação com o desenvolvedor do SDK ou um muita busca on-line por uma solução em potencial, verificar se há uma solução do SDK resolve o ANR para outras pessoas ou até mesmo testando um pequeno estratégia de lançamento.
O Google fornece uma página do SDK Index que combina dados de uso dos apps do Google Play com informações coletadas pela detecção de código para oferecem atributos e indicadores que vão ajudar você a decidir manter ou remover um SDK do seu app.
Outros recursos
Para saber mais sobre ANRs, consulte os seguintes recursos:
- Depurar ANRs: desenvolvimento de jogos Android
- ANRs: qualidade do app