Como ler o estado da rede

O Android permite que os apps aprendam sobre mudanças dinâmicas na conectividade. Use as classes a seguir para monitorar e responder a mudanças de conectividade:

  • ConnectivityManager informa o app sobre o estado da conectividade no sistema.
  • A classe Network representa uma das redes a que o dispositivo está conectado no momento. Use o objeto Network como uma chave para coletar informações sobre a rede com ConnectivityManager ou para vincular soquetes na rede. Quando a rede é desconectada, o objeto Network não pode mais ser usado, mesmo que o dispositivo se conecte novamente à mesma rede. Um novo objeto Network representará a nova rede.
  • O objeto LinkProperties contém informações sobre o link de uma rede, como a lista de servidores DNS, endereços IP locais e as rotas instaladas na rede.
  • O objeto NetworkCapabilities contém informações sobre as propriedades de uma rede, como os transportes (Wi-Fi, celular, Bluetooth), e os recursos da rede. Por exemplo, você pode consultar o objeto para determinar se a rede é capaz de enviar MMS, se tem um portal cativo e se é limitada.

Os apps interessados no estado imediato da conectividade, podem chamar métodos ConnectivityManager a qualquer momento para descobrir que tipo de rede está disponível. Esses métodos são úteis para depuração e, ocasionalmente, para analisar um snapshot de conectividade disponível a qualquer momento. No entanto, os métodos síncronos ConnectivityManager não informam o app sobre nada que acontece após uma chamada. Eles não permitem atualizar a IU nem ajustar o comportamento do app com base na desconexão da rede ou quando os recursos da rede mudarem.

Como a conectividade pode mudar a qualquer momento e a maioria dos apps precisa ter uma visualização sempre atualizada do estado da rede no dispositivo, os apps podem registrar um callback com ConnectivityManager para serem alertados sobre mudanças importantes para eles. Ao usar o callback, o app pode reagir imediatamente a qualquer mudança relevante da conectividade sem precisar recorrer a pesquisas custosas que podem não refletir atualizações rápidas.

Como receber o estado instantâneo

Um dispositivo Android pode manter muitas conexões ao mesmo tempo. Primeiro, acesse uma instância do ConnectivityManager:

Kotlin

val connectivityManager = getSystemService(ConnectivityManager::class.java)

Java

ConnectivityManager connectivityManager = getSystemService(ConnectivityManager.class);

Use esta instância para acessar uma referência à rede padrão atual do app:

Kotlin

val currentNetwork = connectivityManager.getActiveNetwork()

Java

Network currentNetwork = connectivityManager.getActiveNetwork();

Com uma referência a uma rede, o app poderá consultar informações sobre ela.

Kotlin

val caps = connectivityManager.getNetworkCapabilities(currentNetwork)
val linkProperties = connectivityManager.getLinkProperties(currentNetwork)

Java

NetworkCapabilities caps = connectivityManager.getNetworkCapabilities(currentNetwork);
LinkProperties linkProperties = connectivityManager.getLinkProperties(currentNetwork);

A consulta de estado instantâneo não é útil para a maioria dos apps, exceto para depuração. Para recursos mais úteis, como receber alertas quando novas redes forem disponibilizadas e se redes forem desconectadas, registre um NetworkCallback. Para ver mais informações sobre como registrar callbacks de rede, consulte Como detectar eventos de rede.

NetworkCapabilities e LinkProperties

Os objetos NetworkCapabilities e LinkProperties fornecem informações sobre todos os atributos que o sistema sabe sobre uma rede. O objeto LinkProperties sabe sobre rotas, endereços de link, nome da interface, informações do proxy (se houver) e servidores DNS. Chame o método relevante no objeto LinkProperties para recuperar as informações necessárias. O objeto NetworkCapabilities encapsula informações sobre os transportes de rede e os respectivos recursos.

Um transporte é uma abstração de um meio físico pelo qual uma rede opera. Exemplos comuns de transportes são Ethernet, Wi-Fi e celular, mas também podem incluir VPN ou Wi-Fi ponto a ponto.

No Android, uma rede pode ter vários transportes ao mesmo tempo. Um exemplo disso seria uma VPN operando em redes Wi-Fi e celulares. Essa rede teria os transportes Wi-Fi, celular e VPN. Para descobrir se uma rede tem um transporte específico, use NetworkCapabilities.hasTransport(int) com uma das constantes NetworkCapabilities.TRANSPORT_*.

Um recurso descreve uma propriedade da rede. Os recursos de exemplo incluem MMS, NOT_METERED e INTERNET. Uma rede com o recurso de MMS pode enviar e receber mensagens de serviços de mensagens multimídia, enquanto uma rede sem esse recurso não pode. Uma rede com o recurso NOT_METERED não cobra o usuário pelo uso de dados. O app pode verificar os recursos apropriados usando o método NetworkCapabilities.hasCapability(int) com uma das constantes NetworkCapabilities.NET_CAPABILITY_*.

As constantes NET_CAPABILITY_* mais úteis incluem:

  • NET_CAPABILITY_INTERNET: esse recurso indica que a rede está configurada para acessar a Internet. Isso está relacionado à configuração, e não à capacidade real de acessar servidores públicos. Por exemplo, uma rede pode estar configurada para acessar a Internet, mas estar sujeita a um portal cativo.

    A rede celular de uma operadora costuma ter o recurso INTERNET, enquanto uma rede Wi-Fi P2P local normalmente não tem. Para ver a conectividade real, consulte NET_CAPABILITY_VALIDATED.

  • NET_CAPABILITY_NOT_METERED: esse recurso indica que a rede não é limitada. Uma rede é classificada como limitada quando o usuário precisa se preocupar com o uso intenso de dados nessa conexão devido a custos monetários, limitações de dados ou problemas de bateria/desempenho.

  • NET_CAPABILITY_NOT_VPN: esse recurso indica que a rede não é uma rede privada virtual.

  • NET_CAPABILITY_VALIDATED: esse recurso indica que, na última vez que foi sondada, foi confirmado que a rede pode fornecer acesso real à Internet pública. Uma rede protegida por um portal cativo ou uma rede que não fornece resolução de nome de domínio não tem esse recurso. Esse é o sistema mais acessível que pode fornecer informações sobre uma rede que está realmente fornecendo acesso. No entanto, uma rede validada ainda pode, a princípio, estar sujeita a filtragem baseada em IP ou sofrer perdas repentinas de conectividade devido a problemas, como sinal fraco.

  • NET_CAPABILITY_CAPTIVE_PORTAL: esse recurso indica que, na última vez que foi sondada, foi detectado que a rede tem um portal cativo.

Há outros recursos pelos quais apps mais especializados podem se interessar. Leia as definições de parâmetro em NetworkCapabilities.hasCapability(int) para ver mais informações.

Os recursos de uma rede podem mudar a qualquer momento. Quando o sistema detectar um portal cativo, ele mostrará uma notificação pedindo que o usuário a faça login. Enquanto isso estiver em andamento, a rede terá os recursos NET_CAPABILITY_INTERNET e NET_CAPABILITY_CAPTIVE_PORTAL, mas não o NET_CAPABILITY_VALIDATED. Quando o usuário realizar ações e fizer login na página do portal cativo, o dispositivo poderá acessar a Internet pública. A rede passará a ter o recurso NET_CAPABILITY_VALIDATED e perderá o recurso NET_CAPABILITY_CAPTIVE_PORTAL. Da mesma forma, os transportes de uma rede podem mudar dinamicamente. Por exemplo, uma VPN pode ser reconfigurada para usar uma rede mais rápida que acabou de ficar disponível, como alternar a rede atual de celular para Wi-Fi. Nesse caso, a rede perderá o transporte TRANSPORT_CELLULAR e receberá o transporte TRANSPORT_WIFI, mantendo o transporte TRANSPORT_VPN.

Como detectar eventos de rede

Para saber mais sobre eventos de rede, use a classe NetworkCallback com ConnectivityManager.registerDefaultNetworkCallback(NetworkCallback) e ConnectivityManager.registerNetworkCallback(NetworkCallback). Esses dois métodos têm propósitos distintos.

Todos os apps Android têm uma rede padrão. O sistema decide qual será a rede padrão. Normalmente, o sistema prefere usar, como padrão, as redes ilimitadas em vez das limitadas, e as redes mais rápidas em vez das lentas. Quando um app enviar uma solicitação de rede, como HttpsURLConnection, o sistema completa essa solicitação usando a rede padrão. Os apps também podem enviar tráfego em outras redes. Para ver mais informações, consulte Mais Redes.

A rede definida como padrão pode mudar a qualquer momento durante o ciclo de vida de um app. Um exemplo típico seria o dispositivo entrando no alcance de um ponto de acesso Wi-Fi ilimitado, ativo e mais rápido que o sinal da rede celular. O dispositivo se conectaria a esse ponto de acesso e alternaria a rede padrão de todos os apps para a nova rede Wi-Fi.

Quando uma nova rede se tornar a rede padrão, qualquer nova conexão aberta por um app usará essa rede. Em algum momento posterior, todas as conexões restantes na rede padrão anterior serão encerradas à força. Se for importante que o app saiba quando a rede padrão mudar, ele precisará registrar um callback de rede padrão da seguinte forma:

Kotlin

connectivityManager.registerDefaultNetworkCallback(object : ConnectivityManager.NetworkCallback() {
    override fun onAvailable(network : Network) {
        Log.e(TAG, "The default network is now: " + network)
    }

    override fun onLost(network : Network) {
        Log.e(TAG, "The application no longer has a default network. The last default network was " + network)
    }

    override fun onCapabilitiesChanged(network : Network, networkCapabilities : NetworkCapabilities) {
        Log.e(TAG, "The default network changed capabilities: " + networkCapabilities)
    }

    override fun onLinkPropertiesChanged(network : Network, linkProperties : LinkProperties) {
        Log.e(TAG, "The default network changed link properties: " + linkProperties)
    }
})

Java

connectivityManager.registerDefaultNetworkCallback(new ConnectivityManager.NetworkCallback() {
    @Override
    public void onAvailable(Network network) {
        Log.e(TAG, "The default network is now: " + network);
    }

    @Override
    public void onLost(Network network) {
        Log.e(TAG, "The application no longer has a default network. The last default network was " + network);
    }

    @Override
    public void onCapabilitiesChanged(Network network, NetworkCapabilities networkCapabilities) {
        Log.e(TAG, "The default network changed capabilities: " + networkCapabilities);
    }

    @Override
    public void onLinkPropertiesChanged(Network network, LinkProperties linkProperties) {
        Log.e(TAG, "The default network changed link properties: " + linkProperties);
    }
});

Quando uma nova rede se tornar a rede padrão, o app receberá uma chamada de onAvailable(Network) para a nova rede. Implemente onCapabilitiesChanged(Network,NetworkCapabilities), onLinkPropertiesChanged(Network,LinkProperties) ou ambos para reagir de maneira apropriada a mudanças na conectividade.

Para um callback registrado com registerDefaultNetworkCallback(), onLost() significa que a rede perdeu o status de rede padrão. Ela pode ou não ter sido desconectada.

Para saber mais sobre os transportes que a rede padrão está usando, consulte NetworkCapabilities.hasTransport(int). Esse é um proxy ruim em relação à largura de banda ou redes limitadas. O app não pode presumir que o Wi-Fi sempre seja ilimitado e sempre forneça uma largura de banda melhor do que a rede celular. Em vez disso, use NetworkCapabilities.getLinkDownstreamBandwidthKbps() para medir a largura de banda e NetworkCapabilites.hasCapability(int) com argumentos NET_CAPABILITY_NOT_METERED para determinar a limitação da rede. Consulte NetworkCapabilities e LinkProperties para ver mais informações.

Por padrão, os métodos de callback são chamados na linha de execução de conectividade do app, que é uma linha de execução separada usada por ConnectivityManager. Se a implementação dos callbacks precisar realizar trabalhos mais longos, chame-os em uma linha de execução de worker separada usando a variante ConnectivityManager.registerDefaultNetworkCallback(NetworkCallback, Handler).

Cancele o registro do seu callback quando não precisar mais usá-lo chamando ConnectivityManager.unregisterNetworkCallback(NetworkCallback). O método onPause() da sua atividade principal é um bom lugar para fazer isso, especialmente se você registrar o callback em onResume().

Mais redes

Embora a rede padrão seja a única rede relevante para a maioria dos apps, alguns apps podem precisar de outras redes disponíveis. Para saber mais sobre essas redes, os apps criam um NetworkRequest que corresponde às necessidades deles e chamam ConnectivityManager.registerNetworkCallback(NetworkRequest, NetworkCallback). O processo é semelhante a detecção de uma rede padrão. A principal diferença é que, embora possa haver apenas uma rede padrão que se aplique a um app por vez, essa versão permite que o app veja todas as redes disponíveis simultaneamente, portanto, uma chamada para onLost(Network) significa que a rede foi completamente desconectada, e não que ela não é mais a rede padrão.

O app cria uma NetworkRequest para informar ao ConnectivityManager sobre tipo de rede que ele quer detectar. Por exemplo, se o app estiver interessado apenas em conexões de Internet ilimitadas:

Kotlin

val request = NetworkRequest.Builder()
  .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED)
  .addCapability(NET_CAPABILITY_INTERNET)
  .build()

connectivityManager.registerNetworkCallback(request, myNetworkCallback)

Java

NetworkRequest request = new NetworkRequest.Builder()
  .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED)
  .addCapability(NET_CAPABILITY_INTERNET)
  .build();

connectivityManager.registerNetworkCallback(request, myNetworkCallback);

Isso garante que o app detecte todas as mudanças relacionadas a qualquer rede ilimitada no sistema.

Para o callback de rede padrão, há uma versão de registerNetworkCallback(NetworkRequest, NetworkCallback, Handler) que aceita um Handler para não carregar a linha de execução Connectivity do app. Chame ConnectivityManager.unregisterNetworkCallback(NetworkCallback) quando o callback não for mais relevante. Um app pode registrar simultaneamente vários callbacks de rede.

Por conveniência, o objeto NetworkRequest contém os recursos comuns que a maioria dos apps precisa, incluindo:

Ao escrever o código do app, verifique os padrões para ver se eles correspondem ao seu caso de uso e apague-os caso o app também precise ser notificado sobre redes que não têm esses recursos. Por outro lado, seu app precisa adicionar recursos para evitar ser chamado para qualquer mudança de conectividade nas redes com as quais o app não interage.

Por exemplo, se o app precisar enviar mensagens MMS, adicione NET_CAPABILITY_MMS à NetworkRequest para evitar receber notificações sobre todas as redes que não podem enviar mensagens MMS ou TRANSPORT_WIFI_AWARE se ele estiver interessado apenas na conectividade Wi-Fi P2P. NET_CAPABILITY_INTERNET e NET_CAPABILITY_VALIDATED são úteis se você tiver interesse em transferir dados com um servidor na Internet.

Um exemplo

Esta seção descreve a sequência de callbacks que um app pode receber se registrar um callback padrão e um callback normal em um dispositivo que tenha conectividade móvel. Neste exemplo, o dispositivo se conecta a um bom ponto de acesso Wi-Fi e depois se desconecta. A seção também pressupõe que o dispositivo tenha ativado a configuração Dados móveis sempre ativados.

O cronograma é o seguinte:

  1. Quando o app chamar registerNetworkCallback(), o callback receberá imediatamente chamadas de onAvailable(), onNetworkCapabilitiesChanged() e onLinkPropertiesChanged() da rede celular, porque apenas essa rede está disponível. Se outra rede estivesse disponível, o app também teria recebido callbacks para a outra rede.

    Diagrama de estado mostrando o registro do evento do callback da rede e os callbacks acionados pelo evento
    Figura 1. Estado do app depois de chamar registerNetworkCallback().

  2. Depois, o app chamará registerDefaultNetworkCallback(). O callback de rede padrão começa a receber chamadas para onAvailable(), onNetworkCapabilitiesChanged() e onLinkPropertiesChanged() para a rede celular porque ela é a rede padrão. Se outra rede não padrão estivesse ativa, o app não teria recebido chamadas para a rede não padrão.

    Diagrama de estado que mostra o registro do evento de callback da rede padrão e
os callbacks acionados pelo evento
    Figura 2. Estado do app depois de registrar uma rede padrão.

  3. Em seguida, o dispositivo se conectará a uma rede Wi-Fi (ilimitada). O callback normal da rede recebe chamadas para onAvailable(), onNetworkCapabilitiesChanged() e onLinkPropertiesChanged() para a rede Wi-Fi.

    Diagrama de estado mostrando os callbacks acionados quando o app se conecta a uma
nova rede
    Figura 3. Estado do app depois de se conectar a uma rede Wi-Fi ilimitada.

  4. Talvez leve algum tempo até que a rede Wi-Fi seja validada. Nesse caso, as chamadas onNetworkCapabilitiesChanged() para o callback normal da rede não incluiriam o recurso NET_CAPABILITY_VALIDATED. Depois de um curto período, ele receberia uma chamada para onNetworkCapabilitiesChanged(), em que os novos recursos incluiriam NET_CAPABILITY_VALIDATED. Na maioria dos casos, a validação é muito rápida.

    Quando a rede Wi-Fi é validada, o sistema a prioriza em relação à rede celular, principalmente porque é ilimitada. A rede Wi-Fi se tornará a rede padrão. Portanto, o callback da rede padrão receberá uma chamada para onAvailable(), onNetworkCapabilitiesChanged() e onLinkPropertiesChanged() da rede Wi-Fi. A rede celular será movida para o segundo plano, e o callback de rede normal receberá uma chamada onLosing() da rede celular.

    Como este exemplo supõe que os dados móveis estejam sempre ativados neste dispositivo, a rede móvel nunca é desconectada. Se essa configuração estivesse desativada, depois de um tempo a rede celular seria desconectada e o callback de rede normal receberia uma chamada para onLost().

    Diagrama de estado mostrando os callbacks acionados quando uma conexão
de rede Wi-Fi é validada
    Figura 4. Estado do app após a validação da rede Wi-Fi.

  5. Mais tarde, o dispositivo se desconecta do Wi-Fi subitamente porque saiu do alcance. Como o Wi-Fi está desconectado, o callback de rede normal receberá uma chamada onLost() da conexão Wi-Fi. Como a rede celular passa a ser nova rede padrão, o callback de rede padrão receberá chamadas onAvailable(), onNetworkCapabilitiesChanged() e onLinkPropertiesChanged() da rede celular.

    Diagrama de estado mostrando os callbacks acionados quando uma conexão
de rede Wi-Fi é perdida
    Figura 5. Estado do app após a desconexão da rede Wi-Fi.

Se a configuração Dados móveis sempre ativados estivesse desativada, quando o Wi-Fi fosse desconectado, o dispositivo tentaria se reconectar à rede celular. O cenário seria semelhante, mas com um pequeno atraso extra para as chamadas onAvailable(), e o callback de rede normal também receberia chamadas para onAvailable(), onNetworkCapabilitiesChanged() e onLinkPropertiesChanged() porque a rede celular seria a única disponível.

Restrições do uso da rede para transferência de dados

Conseguir detectar uma rede com um callback de rede não significa que seu app pode usá-la para transferência de dados. Algumas redes não oferecem conectividade com a Internet (consulte NET_CAPABILITY_INTERNET e NET_CAPABILITY_VALIDATED para verificar a conectividade com a Internet) e algumas redes podem estar restritas a apps privilegiados.

O uso de redes em segundo plano também está sujeito a verificações de permissão. Se o app quiser usar uma rede em segundo plano, ele precisará da permissão CHANGE_NETWORK_STATE. Os apps com essa permissão também podem pedir que o sistema tente abrir uma rede que não está ativa no momento, como a rede celular quando o dispositivo estiver conectado a uma rede Wi-Fi. Esse app chamaria ConnectivityManager.requestNetwork(NetworkRequest, NetworkCallback) com um NetworkCallback para ser chamado quando a rede estiver ativa.