Lettura dello stato della rete

Android consente alle app di conoscere le modifiche dinamiche alla connettività. Utilizza le seguenti classi per monitorare e rispondere alle modifiche alla connettività:

  • ConnectivityManager comunica all'app lo stato della connettività nel sistema.
  • La classe Network rappresenta una delle reti a cui è connesso il dispositivo. Puoi utilizzare l'oggetto Network come chiave per raccogliere informazioni sulla rete con ConnectivityManager o per associare socket alla rete. Quando la rete si disconnette, l'oggetto Network non è più utilizzabile. Anche se il dispositivo si riconnette in un secondo momento allo stesso elettrodomestico, un nuovo oggetto Network rappresenta la nuova rete.
  • L'oggetto LinkProperties contiene informazioni sul link per una rete, ad esempio l'elenco dei server DNS, gli indirizzi IP locali e le route di rete installate per la rete.
  • L'oggetto NetworkCapabilities contiene informazioni sulle proprietà di una rete, ad esempio i trasporti (Wi-Fi, rete mobile, Bluetooth) e le funzionalità della rete. Ad esempio, puoi eseguire una query sull'oggetto per determinare se la rete è in grado di inviare MMS, si trova dietro un captive portal o è a consumo.

Le app interessate allo stato immediato della connettività in un determinato momento possono chiamare i metodi ConnectivityManager per scoprire quale tipo di rete è disponibile. Questi metodi sono utili per il debug e per rivedere occasionalmente uno snapshot della connettività disponibile in un determinato momento.

Tuttavia, i metodi sincroni ConnectivityManager non comunicano alla tua app nulla di ciò che accade dopo una chiamata, quindi non ti consentono di aggiornare la UI. Inoltre, non possono regolare il comportamento dell'app in base alla disconnessione della rete o alla modifica delle funzionalità di rete.

La connettività può cambiare in qualsiasi momento e la maggior parte delle app deve avere una visualizzazione sempre aggiornata dello stato del networking sul dispositivo. Le app possono registrare un callback con ConnectivityManager per ricevere avvisi sulle modifiche che le interessano. Utilizzando il callback, la tua app può reagire immediatamente a qualsiasi modifica pertinente della connettività, senza dover ricorrere a costose operazioni di polling che potrebbero non rilevare aggiornamenti rapidi.

L'utilizzo di NetworkCallback e altri modi per scoprire lo stato della connettività del dispositivo non richiede alcuna autorizzazione particolare. Tuttavia, alcune emittenti sono soggette ad autorizzazioni specifiche. Ad esempio, potrebbero esserci reti con limitazioni non disponibili per le app. Il binding a una rete in background richiede l'autorizzazione CHANGE_NETWORK_STATE. e alcune richieste potrebbero richiedere autorizzazioni specifiche per essere eseguite. Per i dettagli, consulta la documentazione specifica di ogni chiamata.

Get instantaneous state

Un dispositivo basato su Android può mantenere molte connessioni contemporaneamente. Per ottenere informazioni sullo stato attuale della rete, innanzitutto ottieni un'istanza di ConnectivityManager:

Kotlin

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

Java

ConnectivityManager connectivityManager = getSystemService(ConnectivityManager.class);

Successivamente, utilizza questa istanza per ottenere un riferimento alla rete predefinita corrente per la tua app:

Kotlin

val currentNetwork = connectivityManager.getActiveNetwork()

Java

Network currentNetwork = connectivityManager.getActiveNetwork();

Con un riferimento a una rete, la tua app può richiedere informazioni al riguardo:

Kotlin

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

Java

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

Per funzionalità più utili, registra un NetworkCallback. Per saperne di più sulla registrazione dei callback di rete, consulta Ascoltare gli eventi di rete.

NetworkCapabilities e LinkProperties

Gli oggetti NetworkCapabilities e LinkProperties forniscono informazioni su tutti gli attributi che il sistema conosce di una rete.

L'oggetto LinkProperties conosce le route, gli indirizzi dei link, il nome dell'interfaccia, le informazioni sul proxy (se presenti) e i server DNS. Chiama il metodo pertinente sull'oggetto LinkProperties per recuperare le informazioni che ti servono.

L'oggetto NetworkCapabilities contiene informazioni sui trasporti di rete e sulle relative funzionalità.

Un trasporto è un'astrazione di un mezzo fisico su cui opera una rete. Esempi comuni di trasporti sono Ethernet, Wi-Fi e mobile. Anche le VPN e il Wi-Fi peer-to-peer possono essere trasporti. Su Android, una rete può avere più trasporti contemporaneamente. Un esempio è una VPN che funziona sia su reti Wi-Fi che su reti mobili. La VPN ha i trasporti Wi-Fi, mobile e VPN. Per scoprire se una rete ha un determinato trasporto, utilizza il metodo NetworkCapabilities.hasTransport(int) con una delle costanti NetworkCapabilities.TRANSPORT_*.

Una funzionalità descrive una proprietà della rete. Esempi di funzionalità includono MMS, NOT_METERED e INTERNET. Una rete con la funzionalità MMS può inviare e ricevere messaggi del servizio di messaggistica multimediale, mentre una rete senza questa funzionalità non può. Una rete con la funzionalità NOT_METERED non addebita all'utente i dati. La tua app può verificare le funzionalità appropriate utilizzando il metodo NetworkCapabilities.hasCapability(int) con una delle costanti NetworkCapabilities.NET_CAPABILITY_*.

Le costanti NET_CAPABILITY_* più utili includono:

  • NET_CAPABILITY_INTERNET: indica che la rete è configurata per accedere a internet. Si tratta di configurazione e non di capacità effettiva di raggiungere i server pubblici. Ad esempio, una rete può essere configurata per accedere a internet, ma essere soggetta a un captive portal.

    La rete mobile di un operatore in genere ha la funzionalità INTERNET, mentre una rete Wi-Fi P2P locale in genere non ce l'ha. Per la connettività effettiva, vedi NET_CAPABILITY_VALIDATED.

  • NET_CAPABILITY_NOT_METERED: indica che la rete non è a consumo. Una rete viene classificata come a consumo quando l'utente è sensibile a un utilizzo elevato dei dati su quella connessione a causa di costi monetari, limitazioni dei dati o problemi di prestazioni della batteria.

  • NET_CAPABILITY_NOT_VPN: indica che la rete non è una rete privata virtuale.

  • NET_CAPABILITY_VALIDATED: indica che la rete fornisce l'accesso effettivo a internet pubblico quando viene sottoposta a probe. Una rete dietro un captive portal o una rete che non fornisce la risoluzione dei nomi di dominio non dispone di questa funzionalità. Questo è il livello di precisione più elevato che il sistema può fornire in merito a una rete che fornisce effettivamente l'accesso, anche se una rete convalidata può comunque, in linea di principio, essere soggetta a filtri basati su IP o subire improvvise perdite di connettività a causa di problemi come un segnale debole.

  • NET_CAPABILITY_CAPTIVE_PORTAL: indica che la rete ha un captive portal quando viene sottoposta a probe.

Esistono altre funzionalità che potrebbero interessare app più specializzate. Per saperne di più, leggi le definizioni dei parametri in NetworkCapabilities.hasCapability(int).

Le funzionalità di una rete possono cambiare in qualsiasi momento. Quando il sistema rileva un captive portal, mostra una notifica che invita l'utente ad accedere. Durante questo periodo, la rete dispone delle funzionalità NET_CAPABILITY_INTERNET e NET_CAPABILITY_CAPTIVE_PORTAL, ma non di quella NET_CAPABILITY_VALIDATED.

Quando l'utente esegue un'azione e accede alla pagina del captive portal, il dispositivo può accedere a internet pubblico e la rete acquisisce la funzionalità NET_CAPABILITY_VALIDATED e perde la funzionalità NET_CAPABILITY_CAPTIVE_PORTAL.

Allo stesso modo, i trasporti di una rete possono cambiare dinamicamente. Ad esempio, una VPN può riconfigurarsi per utilizzare una rete più veloce appena disponibile, ad esempio passando dalla rete mobile al Wi-Fi per la rete sottostante. In questo caso, la rete perde il trasporto TRANSPORT_CELLULAR e acquisisce il trasporto TRANSPORT_WIFI, mantenendo il trasporto TRANSPORT_VPN.

Ascolta gli eventi di rete

Per scoprire di più sugli eventi di rete, utilizza la classe NetworkCallback insieme a ConnectivityManager.registerDefaultNetworkCallback(NetworkCallback) e ConnectivityManager.registerNetworkCallback(NetworkCallback). Questi due metodi hanno scopi diversi.

Tutte le app per Android hanno una rete predefinita, che viene determinata dal sistema. Il sistema in genere preferisce le reti non a consumo a quelle a consumo e le reti più veloci a quelle più lente.

Quando un'app invia una richiesta di rete, ad esempio con HttpsURLConnection, il sistema soddisfa questa richiesta utilizzando la rete predefinita. Le app possono inviare traffico anche su altre reti. Per ulteriori informazioni, consulta la sezione relativa alle reti aggiuntive.

La rete impostata come predefinita può cambiare in qualsiasi momento durante il ciclo di vita di un'app. Un esempio tipico è il dispositivo che rientra nel raggio d'azione di un punto di accesso Wi-Fi noto, attivo, senza costi e più veloce della rete mobile. Il dispositivo si connette a questo punto di accesso e cambia la rete predefinita per tutte le app con la nuova rete Wi-Fi.

Quando una nuova rete diventa quella predefinita, qualsiasi nuova connessione aperta dall'app utilizza questa rete. In un secondo momento, tutte le connessioni rimanenti sulla rete predefinita precedente vengono interrotte forzatamente. Se è importante che l'app sappia quando cambia la rete predefinita, registra un callback della rete predefinita come segue:

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 una nuova rete diventa quella predefinita, l'app riceve una chiamata a onAvailable(Network) per la nuova rete. Implementa onCapabilitiesChanged(Network,NetworkCapabilities), onLinkPropertiesChanged(Network,LinkProperties) o entrambi per reagire in modo appropriato ai cambiamenti di connettività.

Per un callback registrato con registerDefaultNetworkCallback(), onLost() significa che la rete ha perso lo stato di rete predefinita. Potrebbe essere disconnesso.

Anche se puoi scoprire i trasporti utilizzati dalla rete predefinita eseguendo una query su NetworkCapabilities.hasTransport(int), questo è un proxy scadente per la larghezza di banda o la misurazione della rete. La tua app non può presupporre che la rete Wi-Fi sia sempre senza costi e fornisca sempre una larghezza di banda migliore rispetto alla rete mobile.

Utilizza invece NetworkCapabilities.getLinkDownstreamBandwidthKbps() per misurare la larghezza di banda e NetworkCapabilites.hasCapability(int) con gli argomenti NET_CAPABILITY_NOT_METERED per determinare la misurazione. Per ulteriori informazioni, consulta la sezione relativa a NetworkCapabilities e LinkProperties.

Per impostazione predefinita, i metodi di callback vengono chiamati sul thread di connettività dell'app, che è un thread separato utilizzato da ConnectivityManager. Se la tua implementazione dei callback deve svolgere un lavoro più lungo, chiamali su un thread di lavoro separato utilizzando la variante ConnectivityManager.registerDefaultNetworkCallback(NetworkCallback, Handler).

Annulla la registrazione del richiamo quando non ti serve più chiamando il numero ConnectivityManager.unregisterNetworkCallback(NetworkCallback). L'onPause() dell'attività principale è un buon punto di partenza, soprattutto se registri il callback in onResume().

Reti aggiuntive (casi d'uso avanzati)

Sebbene la rete predefinita sia l'unica rete pertinente per la maggior parte delle app, alcune app potrebbero essere interessate ad altre reti disponibili. Per scoprirlo, le app creano un NetworkRequest in base alle loro esigenze e chiamano ConnectivityManager.registerNetworkCallback(NetworkRequest, NetworkCallback).

La procedura è simile all'ascolto di una rete predefinita. Tuttavia, anche se in un determinato momento potrebbe essere presente una sola rete predefinita applicabile a un'app, questa versione consente all'app di visualizzare tutte le reti disponibili contemporaneamente, quindi una chiamata a onLost(Network) significa che la rete è stata disconnessa definitivamente, non che non è più la rete predefinita.

L'app crea un NetworkRequest per informare ConnectivityManager del tipo di reti che vuole ascoltare. L'esempio seguente mostra come creare un NetworkRequest per un'app interessata solo alle connessioni internet senza tariffa a consumo:

Kotlin

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

connectivityManager.registerNetworkCallback(request, myNetworkCallback)

Java

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

connectivityManager.registerNetworkCallback(request, myNetworkCallback);

Ciò significa che la tua app viene informata di tutte le modifiche relative a qualsiasi rete non a consumo sul sistema.

Per quanto riguarda il callback di rete predefinito, esiste una versione di registerNetworkCallback(NetworkRequest, NetworkCallback, Handler) che accetta un Handler, in modo da non caricare il thread Connectivity della tua app.

Chiama ConnectivityManager.unregisterNetworkCallback(NetworkCallback) quando la richiamata non è più pertinente. Un'app può registrare contemporaneamente più callback di rete.

Per comodità, l'oggetto NetworkRequest contiene le funzionalità comuni di cui la maggior parte delle app ha bisogno, tra cui:

Quando scrivi la tua app, controlla i valori predefiniti per vedere se corrispondono al tuo caso d'uso e cancellali se vuoi che la tua app riceva notifiche sulle reti che non dispongono di queste funzionalità. D'altra parte, aggiungi funzionalità per evitare di essere chiamato per qualsiasi modifica della connettività nelle reti con cui la tua app non interagisce.

Ad esempio, se la tua app deve inviare messaggi MMS, aggiungi NET_CAPABILITY_MMS a NetworkRequest per evitare di ricevere informazioni su tutte le reti che non possono inviare messaggi MMS. Aggiungi TRANSPORT_WIFI_AWARE se la tua app è interessata solo alla connettività Wi-Fi P2P. NET_CAPABILITY_INTERNET e NET_CAPABILITY_VALIDATED sono utili se ti interessa la possibilità di trasferire dati con un server su internet.

Sequenza di richiamata di esempio

Questa sezione descrive la sequenza di callback che un'app potrebbe ricevere se registra sia un callback predefinito sia un callback normale su un dispositivo che dispone di connettività mobile. In questo esempio, il dispositivo si connette a un buon punto di accesso Wi-Fi, poi si disconnette. L'esempio presuppone inoltre che sul dispositivo sia attivata l'impostazione Dati mobili sempre attivi.

La sequenza temporale è la seguente:

  1. Quando l'app chiama registerNetworkCallback(), il callback riceve immediatamente chiamate da onAvailable(), onNetworkCapabilitiesChanged() e onLinkPropertiesChanged() per la rete mobile, perché è l'unica rete disponibile. Se è disponibile un'altra rete, l'app riceve anche i callback per l'altra rete.

    Diagramma di stato che mostra l'evento di callback della rete di registrazione e i callback attivati dall'evento
    Figura 1. Stato dell'app dopo aver chiamato il numero registerNetworkCallback().

  2. Poi, l'app chiama registerDefaultNetworkCallback(). Il callback della rete predefinita inizia a ricevere chiamate a onAvailable(), onNetworkCapabilitiesChanged() e onLinkPropertiesChanged() per la rete mobile, perché è la rete predefinita. Se è attiva un'altra rete non predefinita, l'app non può ricevere chiamate per la rete non predefinita.

    Diagramma di stato che mostra la registrazione dell'evento di callback di rete predefinito e i
callback attivati dall'evento
    Figura 2. Stato dell'app dopo la registrazione di una rete predefinita.

  3. In un secondo momento, il dispositivo si connette a una rete Wi-Fi (senza tariffa a consumo). Il callback di rete normale riceve chiamate ai numeri onAvailable(), onNetworkCapabilitiesChanged() e onLinkPropertiesChanged() per la rete Wi-Fi.

    Diagramma di stato che mostra i callback attivati quando l'app si connette a una
nuova rete
    Figura 3. Stato dell'app dopo la connessione a una rete Wi-Fi senza limiti.

  4. A questo punto, è possibile che la rete Wi-Fi impieghi un po' di tempo per la convalida. In questo caso, i callback onNetworkCapabilitiesChanged() per il callback di rete regolare non includono la funzionalità NET_CAPABILITY_VALIDATED. Dopo un breve periodo di tempo, riceve una chiamata al onNetworkCapabilitiesChanged(), dove le nuove funzionalità includono NET_CAPABILITY_VALIDATED. Nella maggior parte dei casi, la convalida è molto rapida.

    Quando la rete Wi-Fi viene convalidata, il sistema la preferisce alla rete mobile, principalmente perché non è a consumo. La rete Wi-Fi diventa la rete predefinita, quindi il callback della rete predefinita riceve una chiamata a onAvailable(), onNetworkCapabilitiesChanged() e onLinkPropertiesChanged() per la rete Wi-Fi. La rete mobile passa in background e il callback di rete normale riceve una chiamata a onLosing() per la rete mobile.

    Poiché questo esempio presuppone che i dati mobili siano sempre attivi per questo dispositivo, la rete mobile non si disconnette mai. Se l'impostazione è disattivata, dopo un po' di tempo la rete mobile si disconnette e il callback di rete normale riceve una chiamata al numero onLost().

    Diagramma di stato che mostra i callback attivati quando una connessione
alla rete Wi-Fi viene convalidata
    Figura 4. Stato dell'app dopo la convalida della rete Wi-Fi.

  5. Più tardi, il dispositivo si disconnette improvvisamente dal Wi-Fi perché è uscito dal raggio d'azione. Poiché la rete Wi-Fi si disconnette, il normale callback di rete riceve una chiamata a onLost() per il Wi-Fi. Poiché la rete mobile è la nuova rete predefinita, il callback della rete predefinita riceve chiamate ai numeri onAvailable(), onNetworkCapabilitiesChanged() e onLinkPropertiesChanged() per la rete mobile.

    Diagramma di stato che mostra i callback attivati quando viene persa una connessione
a una rete Wi-Fi
    Figura 5. Stato dell'app dopo la disconnessione dalla rete Wi-Fi.

Se l'impostazione Dati mobili sempre attivi è disattivata, quando la connessione Wi-Fi si interrompe, il dispositivo tenta di riconnettersi a una rete mobile. L'immagine è simile, ma con un breve ritardo aggiuntivo per le chiamate onAvailable() e il normale callback di rete riceve anche chiamate ai numeri onAvailable(), onNetworkCapabilitiesChanged() e onLinkPropertiesChanged() perché il cellulare diventa disponibile.

Restrizioni relative all'utilizzo della rete per il trasferimento di dati

La possibilità di vedere una rete con un callback di rete non significa che la tua app possa utilizzare la rete per il trasferimento di dati. Alcune reti non forniscono connettività a internet e alcune potrebbero essere limitate alle app con privilegi. Per verificare la connettività a internet, vedi NET_CAPABILITY_INTERNET e NET_CAPABILITY_VALIDATED.

L'utilizzo delle reti in background è soggetto anche a controlli delle autorizzazioni. Se la tua app vuole utilizzare una rete in background, ha bisogno dell'autorizzazione CHANGE_NETWORK_STATE.

Le app con questa autorizzazione consentono al sistema di tentare di attivare una rete non attiva, ad esempio la rete mobile quando il dispositivo è connesso a una rete Wi-Fi. Un'app di questo tipo chiama ConnectivityManager.requestNetwork(NetworkRequest, NetworkCallback) con un NetworkCallback da chiamare quando la rete viene attivata.