Lettura dello stato della rete

Android consente alle app di rilevare i cambiamenti dinamici della connettività. Utilizza le seguenti classi per monitorare e rispondere alle modifiche alla connettività:

  • ConnectivityManager indica alla tua 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 i socket sulla rete. Quando la rete si disconnette, l'oggetto Network smette di essere utilizzabile. Anche se in un secondo momento il dispositivo si riconnette alla stessa appliance, un nuovo oggetto Network rappresenta la nuova rete.
  • L'oggetto LinkProperties contiene informazioni sul link di una rete, come l'elenco di server DNS, indirizzi IP locali e route di rete installati per la rete.
  • L'oggetto NetworkCapabilities contiene informazioni sulle proprietà di una rete, come i trasferimenti (Wi-Fi, 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, se si trova dietro a un captive portal o se è a consumo.

Le app interessate a conoscere lo 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 un'istantanea della connettività disponibile in un determinato momento.

Tuttavia, i metodi sincroni ConnectivityManager non comunicano alla tua app nulla che accade dopo una chiamata, quindi non ti consentono di aggiornare l'interfaccia utente. Inoltre, non possono modificare il comportamento dell'app in base alla disconnessione della rete o quando le funzionalità di rete cambiano.

La connettività può cambiare in qualsiasi momento e la maggior parte delle app deve avere una visione sempre aggiornata dello stato della rete sul dispositivo. Le app possono registrare un callback con ConnectivityManager per ricevere avvisi sulle modifiche interessate all'app. Con il callback, la tua app può reagire immediatamente a qualsiasi modifica pertinente nella connettività, senza dover ricorrere a sondaggi costosi che potrebbero perdere aggiornamenti rapidi.

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

Ottieni stato istantaneo

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

Kotlin

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

Java

ConnectivityManager connectivityManager = getSystemService(ConnectivityManager.class);

Quindi, 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 a 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 ulteriori informazioni 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 di una rete noti al sistema.

L'oggetto LinkProperties è in grado di conoscere route, indirizzi link, nome dell'interfaccia, informazioni proxy (se presenti) e server DNS. Chiama il metodo pertinente nell'oggetto LinkProperties per recuperare le informazioni necessarie.

L'oggetto NetworkCapabilities incapsula informazioni sui trasporti di rete e sulle loro funzionalità.

Un trasporto è l'astrazione di un mezzo fisico su cui opera una rete. Esempi comuni di trasporti sono Ethernet, Wi-Fi e mobile. Anche le VPN e le reti Wi-Fi peer-to-peer possono essere trasporti. Su Android, una rete può avere più trasporti contemporaneamente. Un esempio è una VPN che opera sia su reti Wi-Fi sia su reti mobili. La VPN include 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. Le funzionalità di esempio includono MMS, NOT_METERED e INTERNET. Una rete con funzionalità MMS può inviare e ricevere messaggi di Multimedia Messaging Service, mentre una rete senza questa funzionalità non può farlo. Una rete con la funzionalità NOT_METERED non fattura all'utente i dati. La tua app può verificare la presenza di 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 della configurazione e non della 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.

    In genere la rete mobile di un operatore ha la funzionalità INTERNET, a differenza di una rete Wi-Fi P2P locale. Per la connettività effettiva, consulta 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 all'utilizzo intensivo 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 alla rete internet pubblica quando viene eseguita il probe. Una rete dietro un captive portal o una rete che non fornisce la risoluzione dei nomi di dominio non offre questa funzionalità. Si tratta dell'entità più vicina che il sistema può rilevare riguardo a una rete che fornisce effettivamente l'accesso, sebbene una rete convalidata possa comunque, in linea di principio, essere soggetta al filtro basato 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 eseguito il probe.

Esistono altre funzionalità che potrebbero interessare alle app più specializzate. Per ulteriori informazioni, 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. Mentre questa è in corso, la rete ha le funzionalità NET_CAPABILITY_INTERNET e NET_CAPABILITY_CAPTIVE_PORTAL ma non la funzionalità NET_CAPABILITY_VALIDATED.

Quando l'utente interviene ed esegue l'accesso alla pagina del captive portal, il dispositivo può accedere alla rete internet pubblica e la rete acquisisce la funzionalità NET_CAPABILITY_VALIDATED, ma perde la funzionalità NET_CAPABILITY_CAPTIVE_PORTAL.

Allo stesso modo, i trasporti di una rete possono cambiare in modo dinamico. Ad esempio, una VPN può riconfigurarsi per utilizzare una rete più veloce appena creata, come il passaggio da rete mobile a Wi-Fi per la rete sottostante. In questo caso, la rete perde il trasporto TRANSPORT_CELLULAR e prende il trasporto TRANSPORT_WIFI, mantenendo il trasporto TRANSPORT_VPN.

Ascoltare 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 Android hanno una rete predefinita, che viene determinata dal sistema. Il sistema in genere preferisce le reti unmetered a quelle a consumo e le reti più veloci a quelle più lente.

Quando un'app invia una richiesta di rete, ad esempio 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 rete predefinita può cambiare in qualsiasi momento durante il ciclo di vita di un'app. Un esempio tipico è il caso in cui il dispositivo rientri nella portata di un punto di accesso Wi-Fi noto, attivo, senza limiti e più veloce dei dispositivi mobili. Il dispositivo si connette a questo punto di accesso e trasferisce la rete predefinita per tutte le app alla nuova rete Wi-Fi.

Quando una nuova rete diventa quella predefinita, qualsiasi nuova connessione aperta dall'app utilizza questa rete. Prima o poi, tutte le connessioni rimanenti sulla rete predefinita precedente vengono terminate in modo forzato. Se è importante che l'app sappia quando cambia la rete predefinita, registra un callback di rete predefinito 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 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 nella 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 ottenere informazioni sui trasporti utilizzati dalla rete predefinita eseguendo una query su NetworkCapabilities.hasTransport(int), questo non è un buon proxy per la larghezza di banda o il consumo della rete. La tua app non può presupporre che il Wi-Fi sia sempre illimitato e offra sempre una larghezza di banda migliore rispetto ai dispositivi mobili.

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

Per impostazione predefinita, i metodi di callback vengono chiamati nel thread di connettività della tua app, che è un thread separato utilizzato da ConnectivityManager. Se la tua implementazione dei callback deve funzionare più a lungo, chiamali in un thread di worker separato utilizzando la variante ConnectivityManager.registerDefaultNetworkCallback(NetworkCallback, Handler).

Annulla la registrazione della richiamata quando non la utilizzi più chiamando il numero ConnectivityManager.unregisterNetworkCallback(NetworkCallback). Per farlo, puoi utilizzare onPause() dell'attività principale, soprattutto se registri il callback in onResume().

Reti aggiuntive

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 saperne di più, le app creano una 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 può essere presente una sola rete predefinita da applicare a un'app, questa versione consente all'app di vedere tutte le reti disponibili contemporaneamente, quindi una chiamata a onLost(Network) significa che la rete si è disconnessa definitivamente, non che non è più la rete predefinita.

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

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);

Ciò significa che l'app viene a conoscenza 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 Handler, in modo da non caricare il thread Connectivity della tua app.

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

Per praticità, l'oggetto NetworkRequest contiene le funzionalità comuni necessarie alla maggior parte delle app, tra cui:

Quando scrivi la tua app, controlla le impostazioni predefinite per vedere se corrispondono al tuo caso d'uso e cancellale se vuoi che l'app riceva notifiche sulle reti che non hanno queste funzionalità. D'altra parte, aggiungi funzionalità per evitare di essere chiamato per qualsiasi modifica di 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 che ti vengano comunicate 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 vuoi poter trasferire dati con un server su internet.

Sequenza di callback di esempio

In questa sezione viene descritta la sequenza di callback che un'app potrebbe ricevere se registra sia un callback predefinito sia un callback normale su un dispositivo dotato di connettività mobile. In questo esempio, il dispositivo si connette a un buon punto di accesso Wi-Fi e poi si disconnette. L'esempio presuppone anche che sul dispositivo sia attiva l'impostazione Dati mobili sempre attivi.

Le tempistiche sono le seguenti:

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

    Diagramma di stato che mostra l'evento di callback della rete della registrazione e i callback attivati dall'evento
    Figura 1. Stato dell'app dopo la chiamata a registerNetworkCallback().

  2. Dopodiché l'app chiama registerDefaultNetworkCallback(). Il callback di rete predefinito inizia a ricevere chiamate a onAvailable(), onNetworkCapabilitiesChanged() e onLinkPropertiesChanged() per la rete mobile, perché la rete mobile è 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 l'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 seguito, il dispositivo si connetterà a una rete Wi-Fi (senza limiti). Il callback di rete normale riceve chiamate a 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 illimitata.

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

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

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

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

  5. Più tardi ancora, il dispositivo si disconnette improvvisamente dalla rete Wi-Fi perché è fuori portata. Poiché la rete Wi-Fi si disconnette, il normale callback di rete riceve una chiamata a onLost() per la connessione Wi-Fi. Poiché la rete mobile è la nuova rete predefinita, il callback di rete predefinito riceve chiamate a onAvailable(), onNetworkCapabilitiesChanged() e onLinkPropertiesChanged() per la rete mobile.

    Diagramma di stato che mostra i callback attivati in caso di interruzione della connessione alla 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 rete Wi-Fi viene disconnessa il dispositivo prova a riconnettersi a una rete mobile. L'immagine è simile, ma con un breve ritardo aggiuntivo per le chiamate onAvailable() e il callback di rete normale riceve chiamate a onAvailable(), onNetworkCapabilitiesChanged() e onLinkPropertiesChanged() perché la funzionalità mobile diventa disponibile.

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

Se riesci a vedere una rete con un callback di rete, non significa che la tua app possa usarla 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.

Anche l'utilizzo di reti in background è soggetto a controlli delle autorizzazioni. Se la tua app vuole utilizzare una rete in background, deve avere l'autorizzazione CHANGE_NETWORK_STATE.

Le app con questa autorizzazione consentono al sistema di tentare di visualizzare una rete non attiva, ad esempio la rete mobile quando il dispositivo è connesso a una rete Wi-Fi. Questa app chiama ConnectivityManager.requestNetwork(NetworkRequest, NetworkCallback) con NetworkCallback per essere chiamata quando viene visualizzata la rete.