android.os.NetworkOnMainThreadException significa que um aplicativo Android tentou executar uma operação de rede na thread principal da interface. O Android lança essa exceção em aplicativos destinados ao SDK Honeycomb/API de nível 11 ou posterior. A primeira medida mais segura não é suprimir a exceção: inspecione o caminho da chamada que falhou, identifique a primeira operação de rede bloqueante pertencente ao aplicativo e determine se a API cliente envolvida é síncrona ou já pode ser chamada com segurança a partir da thread principal.

O que significa android.os.NetworkOnMainThreadException

A exceção exata é android.os.NetworkOnMainThreadException. Segundo a referência de NetworkOnMainThreadException do Android, ela é lançada quando um aplicativo tenta executar uma operação de rede na thread principal. Ela foi adicionada no nível 11 da API e é lançada em aplicativos destinados ao SDK Honeycomb ou posterior.

A operação que aciona a exceção pode ser uma conexão de URL bloqueante, URL.openStream(), acesso a sockets, resolução de nomes ou uma chamada síncrona de cliente. Código com corrotinas também pode acionar a exceção ao executar E/S bloqueante sem mudar para um dispatcher de segundo plano adequado. O fato de uma função ser marcada como suspend não transfere, por si só, o trabalho para fora da thread principal.

O bloqueio da thread principal impede o processamento de eventos da interface enquanto a operação está em execução, criando risco de falta de resposta e de o aplicativo não responder. Aplicativos destinados a níveis anteriores do SDK não estavam sujeitos a essa exceção da mesma forma, mas isso não torna seguro nem recomendável executar operações de rede na thread principal.

Localize a chamada de rede bloqueante no caminho da falha

Use o stack trace da falha reproduzível para localizar a operação síncrona antes de alterar o modelo de execução. Os nomes e a ordem exata dos frames variam conforme o aplicativo e a biblioteca de rede, portanto essa etapa exige confirmação específica no projeto.

  1. Reproduza a operação que informa android.os.NetworkOnMainThreadException e preserve o stack trace completo.
  2. Leia o stack trace a partir da exceção em direção aos frames do aplicativo. Procure o primeiro frame pertencente ao projeto que leve a uma conexão de URL, URL.openStream(), acesso a sockets, resolução de nomes ou uma chamada síncrona de cliente.
  3. Consulte a documentação da biblioteca envolvida para determinar se o método é bloqueante, baseado em callback ou uma API suspensa documentada como segura para chamada a partir da thread principal.
  4. Rastreie o contexto de execução do chamador. Caminhos iniciados por uma atividade, um fragmento, um callback de composable ou viewModelScope normalmente começam na thread principal, mas o caminho real do projeto precisa ser verificado.
  5. Escolha uma das correções correspondentes abaixo, em vez de envolver toda API de rede em outro mecanismo de execução.

O stack trace pode mostrar StrictMode$AndroidBlockGuardPolicy.onNetwork acima de um frame pertencente ao aplicativo. Esse é um padrão de diagnóstico comum, não uma sequência universal de frames. O frame relevante é a chamada do projeto que iniciou a operação bloqueante.

Use o StrictMode somente quando for necessário um diagnóstico adicional durante a depuração

Quando se aplica: use o StrictMode durante testes locais quando a falha existente não revelar claramente um acesso acidental à rede na thread principal.

Pré-requisitos: use uma versão de depuração ou um ambiente de teste local e planeje remover ou flexibilizar a política de diagnóstico após a verificação.

  1. Ative o StrictMode na versão de depuração.
  2. Acione o fluxo que apresenta a falha.
  3. Inspecione a violação informada e identifique a primeira chamada de rede bloqueante pertencente ao aplicativo.
  4. Corrija a thread responsável pela chamada em vez de suprimir a violação.

Resultado esperado: o diagnóstico identifica um caminho pertencente ao aplicativo que pode ser atribuído à API assíncrona, ao dispatcher ou à correção com Executor adequada.

Risco e reversão: o risco é baixo e não há risco esperado de perda de dados. Desative a política exclusiva de depuração depois de verificar a correção. Não use permitAll() nem permitNetwork() como correção em produção.

Escolha a correção correspondente à API de rede

Use o caminho aplicável menos invasivo. Primeiro, determine se o cliente já executa as operações de forma assíncrona. Adicione um dispatcher ou Executor somente quando a operação subjacente for bloqueante.

Correção 1: use diretamente uma API cliente que já seja assíncrona ou segura para a thread principal

Quando se aplica: use este caminho quando a documentação da biblioteca informar que a API de callback ou suspensa já executa o trabalho em segundo plano e pode ser chamada com segurança a partir de código na thread principal.

Pré-requisitos: confirme o comportamento na documentação da biblioteca cliente. Não presuma que todo método marcado como suspend seja seguro para a thread principal.

  1. Prefira a API de callback assíncrona ou a API suspensa segura para a thread principal documentada pela biblioteca.
  2. Chame essa API a partir de código seguro para a thread principal.
  3. Não adicione um wrapper redundante de withContext(Dispatchers.IO) a uma API já documentada como segura para a thread principal.
  4. Retome o trabalho da interface na thread principal depois que a operação for concluída.

Resultado esperado: a biblioteca executa a operação de rede sem bloquear a thread principal do Android, enquanto o chamador processa o resultado no contexto correto da interface.

Risco e reversão: o risco é baixo e não há risco esperado de perda de dados. Se a documentação ou os testes mostrarem que a API não é realmente segura para a thread principal, deixe de usar este caminho e transfira a camada bloqueante para fora de Main. A documentação sobre corrotinas do Kotlin no Android diferencia o código bloqueante que precisa de Dispatchers.IO das APIs de rede que já oferecem comportamento suspenso seguro para a thread principal.

Correção 2: mova operações de rede bloqueantes em Kotlin para withContext(Dispatchers.IO)

Quando se aplica: use este caminho para uma função suspensa do Kotlin que invoque diretamente código bloqueante de rede ou resolução de nomes.

Pré-requisitos: você precisa controlar a função suspensa ou a camada do repositório, e a operação subjacente precisa ser bloqueante, em vez de já ser segura para a thread principal.

  1. Mantenha o chamador em Main quando ele for responsável pelo estado da interface.
  2. Envolva somente o bloco de rede bloqueante em withContext(Dispatchers.IO) ou use o dispatcher de E/S injetado pelo projeto.
  3. Retorne ao chamador apenas o resultado da operação.
  4. Atualize o estado da interface depois que a chamada suspensa terminar e a execução voltar para Main.

Resultado esperado: a E/S bloqueante é executada no dispatcher de E/S, enquanto o estado da interface permanece sob responsabilidade da thread principal.

Risco e reversão: o risco é baixo e não há risco esperado de perda de dados. Remova a mudança de dispatcher somente se for confirmado que a biblioteca oferece uma API suspensa segura para a thread principal. As práticas recomendadas para corrotinas no Android exigem que funções suspensas que possam ser chamadas a partir de Main sejam seguras para a thread principal e observam que viewModelScope normalmente começa em Dispatchers.Main.

Correção 3: use um Executor Java e retorne o resultado para Main

Quando se aplica: use este caminho para código Java que execute diretamente E/S de rede bloqueante.

Pré-requisitos: o aplicativo precisa ter um Executor ou pool de threads em segundo plano e um caminho de callback na thread principal para atualizar a interface.

  1. Envie a tarefa de rede bloqueante ao Executor.
  2. Execute a chamada de rede dentro dessa tarefa em segundo plano, e não na thread principal.
  3. Envie o resultado de volta pelo caminho de callback da thread principal do aplicativo.
  4. Atualize as views somente por meio desse callback na thread principal.

Resultado esperado: a operação bloqueante é executada pelo Executor, e somente o trabalho de interface resultante retorna para Main.

Risco e reversão: o risco é baixo e não há risco esperado de perda de dados. Cancele a tarefa ou deixe de enviar novos trabalhos quando a interface que iniciou a operação não existir mais. Transferir a operação de rede para fora de Main não torna seguro atualizar views a partir da tarefa em segundo plano.

Gerencie o ciclo de vida e o trabalho persistente sem criar outro problema

Atribuir o trabalho à thread correta não torna uma solicitação automaticamente segura em relação ao ciclo de vida. Uma solicitação pode continuar depois que a atividade ou o fragmento for encerrado, retornar para uma interface obsoleta ou duplicar trabalho quando a ação inicial for repetida. As políticas de cancelamento, tratamento de destruição e solicitações duplicadas dependem da arquitetura do aplicativo e precisam ser verificadas no projeto de destino.

  • Confirme o que acontece se a atividade ou o fragmento for destruído enquanto o trabalho ainda estiver ativo.
  • Garanta que o trabalho em segundo plano não atualize views depois que a interface relacionada não existir mais.
  • Verifique se ações repetidas podem enviar solicitações duplicadas.
  • Cancele o trabalho ou deixe de aceitar o resultado quando o ciclo de vida responsável não precisar mais dele.

Use o WorkManager somente para trabalho persistente ou adiável

Quando se aplica: use o WorkManager quando a tarefa de rede puder ser adiada, for de longa duração ou precisar continuar após reinicializações do aplicativo. Ele não é o substituto padrão para toda solicitação imediata iniciada pela interface.

Pré-requisitos: a operação precisa ser adequada para execução como trabalho em segundo plano, e não como uma solicitação imediata vinculada à tela atual.

  1. Crie uma WorkRequest ou um Worker para a operação em segundo plano.
  2. Adicione restrições de rede se a tarefa precisar delas.
  3. Permita que o WorkManager execute a operação fora da thread da interface.
  4. Observe a conclusão e atualize a interface separadamente.

Resultado esperado: o trabalho persistente ou adiável é gerenciado independentemente da tela que o iniciou e não bloqueia a thread principal.

Risco e reversão: o risco é baixo e não há risco esperado de perda de dados. Cancele a WorkRequest quando a tarefa não for mais necessária. Se a solicitação exigir uma resposta imediata para a interface atual, use o caminho aplicável com API assíncrona, corrotina ou Executor.

Verifique se a exceção foi corrigida

  1. Reproduza o fluxo original que apresentava a falha depois de aplicar um caminho de correção adequado.
  2. Confirme que a operação bloqueante de rede ou resolução de nomes não é mais executada na thread principal.
  3. Confirme que as atualizações de views e do estado da interface ocorrem somente depois que o resultado retorna para Main.
  4. Teste o que acontece quando a atividade ou o fragmento que iniciou a operação é destruído antes da conclusão.
  5. Teste entradas repetidas para determinar se solicitações duplicadas são criadas ou controladas corretamente.
  6. Em uma versão de depuração, use o StrictMode para procurar outros acessos acidentais à rede na thread principal e depois restaure a política de depuração pretendida.
  7. Analise o stack trace resultante se outra exceção aparecer. Diagnostique a nova assinatura separadamente, em vez de tratá-la como outra ocorrência de NetworkOnMainThreadException.

A correção de threading estará verificada quando o fluxo original deixar de executar trabalho de rede bloqueante em Main e todas as alterações relacionadas à interface continuarem ocorrendo em Main. Uma resposta bem-sucedida do endpoint, por si só, não comprova que o trabalho foi atribuído à thread correta, e remover essa exceção não garante que a solicitação de rede terá sucesso.

Erros e soluções alternativas que não correspondem a esta correção

Mensagem ou abordagem Por que é diferente
UnknownHostException Essa é uma falha diferente, relacionada à resolução ou acessibilidade do host, e não comprova que o trabalho de rede foi executado em Main.
SSLHandshakeException Essa é uma falha de TLS separada e exige um diagnóstico próprio.
Permissão INTERNET ausente Esse é um problema de permissão ou configuração, não a causa que define NetworkOnMainThreadException.
Falha na política de tráfego não criptografado Essa é uma restrição da política de segurança de rede sobre tráfego não criptografado, não uma violação por execução na thread principal.
CalledFromWrongThreadException Essa exceção se refere ao acesso a uma view pela thread errada depois que o trabalho muda de contexto; não é a mesma exceção.
permitAll() ou permitNetwork() Esses métodos flexibilizam a restrição de diagnóstico em vez de corrigir a responsabilidade pela operação bloqueante.
Orientações obsoletas sobre AsyncTask AsyncTask está obsoleto e não é a correção moderna recomendada.

Não comece investigando TLS, acessibilidade de DNS, permissões ou tráfego não criptografado quando a falha real for android.os.NetworkOnMainThreadException. Primeiro, atribua o trabalho à thread correta. Se a transferência de uma solicitação HTTP para fora de Main revelar uma falha separada de Cleartext HTTP traffic not permitted, diagnostique esse novo erro de forma independente.