ANRs comuns de jogos do Unity

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 app WebView foi movido para um APK. Portanto, pode ser atualizados separadamente da plataforma Android. Para saber qual versão do WebView 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.
.
Tela de informações do app mostrando as versões do WebView.
Figura 1. Verifique a versão do WebView.

Pausa do Unity

Quando UnityPlayerActivity recebe uma chamada onPause(), a seguinte cadeia de operações começa:

  1. UnityPlayerActivity notifica o mecanismo de tempo de execução do Unity que a atividade tem pausado.
  2. O Unity chama cada MonoBehaviour que implementa o OnApplicationPause.
  3. O Unity interrompe os componentes e módulos, como reprodução de som, renderização loop de jogo e animação.
  4. Para garantir que o Unity Android Player (UAP) e o mecanismo forem sincronizados, o UAP esperará quatro segundos para que o mecanismo seja interrompido.
  5. 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 um Stopwatch.
  • Evite operações de E/S ou solicitações de rede síncronas.
  • Mova as operações para outro Thread usando o Task O Unity 2023.1 oferece suporte a uma modelo de programação assíncrona usando C# async e await 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.
  • 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.

Figura 5. Relatório do Android vitals que contém uma chamada de vinculaçã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: