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'oggettoNetwork
come chiave per raccogliere informazioni sulla rete conConnectivityManager
o per eseguire il binding dei socket nella rete. Quando la rete si disconnette, l'oggettoNetwork
non è più utilizzabile. Anche se il dispositivo si riconnette in un secondo momento alla stessa appliance, un nuovo oggettoNetwork
rappresenta la nuova rete. - L'oggetto
LinkProperties
contiene informazioni sul collegamento di 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
ConnectivityManager
sincroni 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 di 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 alla 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 reti 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
chiamate potrebbero richiedere autorizzazioni specifiche per essere eseguite. Per i dettagli, consulta la documentazione specifica di ogni chiamata.
Ottenere lo stato attuale
Un dispositivo basato su Android può mantenere molte connessioni contemporaneamente.
Per ottenere informazioni sullo stato attuale della rete, devi prima ottenere
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 altre funzionalità utili, registra un costruttore
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 del collegamento, 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 reti VPN e 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 mobile. 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 MMS (Multimedia Messaging Service), mentre una rete senza questa
funzionalità non può. Una rete con la funzionalità NOT_METERED
non addebita all'utente
l'utilizzo dei 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 di configurazione e non di capacità effettiva di raggiungere 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, vediNET_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 elevato di 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 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 modo più preciso in cui il sistema può indicare 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 perdite improvvise 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, viene visualizzata una notifica che invita l'utente a eseguire l'accesso. 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 captive portal, il dispositivo può accedere alla rete internet pubblica 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 che è diventata disponibile, ad esempio passando dalla rete mobile a quella Wi-Fi per
la sua rete sottostante. In questo caso, la rete perde il trasporto TRANSPORT_CELLULAR
e acquisisce il trasporto TRANSPORT_WIFI
, mantenendo il trasporto TRANSPORT_VPN
.
Ascoltare 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 saperne di più, 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, non a consumo e più veloce della rete mobile. Il dispositivo si connette a questo punto di accesso e cambia la rete predefinita con la nuova rete Wi-Fi per tutte le app.
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 per l'app è importante sapere 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 quella predefinita, l'app riceve una chiamata a
onAvailable(Network)
per la nuova rete. Implementa
onCapabilitiesChanged(Network,NetworkCapabilities)
,
onLinkPropertiesChanged(Network,LinkProperties)
o entrambi i metodi per reagire in modo appropriato alle modifiche alla connettività.
Per un callback registrato con registerDefaultNetworkCallback()
, il metodo onLost()
significa che la rete ha perso lo stato di rete predefinita. Potrebbe essere disconnessa.
Anche se puoi scoprire i trasporti utilizzati dalla rete predefinita eseguendo una query su
NetworkCapabilities.hasTransport(int)
,
si tratta di un proxy scadente per determinare la larghezza di banda o se la rete non è a consumo. La tua app
non può presupporre che la rete Wi-Fi sia sempre non a consumo 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 se la rete non è a consumo. Per saperne di più, consulta la sezione
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 eseguire un lavoro più lungo, chiamali su un
thread di lavoro separato utilizzando la variante
ConnectivityManager.registerDefaultNetworkCallback(NetworkCallback, Handler)
.
Annulla la registrazione del callback quando non ti serve più chiamando
ConnectivityManager.unregisterNetworkCallback(NetworkCallback)
.
Il metodo onPause()
dell'attività principale
è un buon punto per farlo, soprattutto se registri il callback nel metodo
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 scoprirle, le app
creano un oggetto NetworkRequest
che soddisfi le 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 vedere tutte le reti disponibili contemporaneamente, quindi una chiamata a onLost(Network)
significa che la rete è stata disconnessa definitivamente, non che non è più quella predefinita.
L'app crea un oggetto NetworkRequest
per informare ConnectivityManager
del tipo
di reti che vuole ascoltare. L'esempio seguente mostra come creare un oggetto
NetworkRequest
per un'app interessata solo alle connessioni
internet non 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 il parametro Handler
, in modo da non caricare il thread Connectivity
della tua
app.
Chiama
ConnectivityManager.unregisterNetworkCallback(NetworkCallback)
quando il callback non è più pertinente. Un'app può registrare contemporaneamente
più callback di rete.
Per comodità, l'oggetto
NetworkRequest
contiene le
funzionalità comuni necessarie alla maggior parte delle app, tra cui:
Quando scrivi la tua app, controlla che i valori predefiniti corrispondano al tuo caso d'uso e cancellali se vuoi che la tua app riceva notifiche sulle reti che non dispongono di queste funzionalità. Dall'altro lato, aggiungi funzionalità per evitare di ricevere chiamate per qualsiasi modifica alla connettività nelle reti con cui la tua app non interagisce.
Ad esempio, se la tua app deve inviare messaggi MMS, aggiungi la funzionalità
NET_CAPABILITY_MMS
all'oggetto 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.
Le funzionalità NET_CAPABILITY_INTERNET
e
NET_CAPABILITY_VALIDATED
sono utili se ti interessa la possibilità di trasferire dati con un server
su internet.
Esempio di sequenza di callback
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 con connettività mobile. In questo esempio, il dispositivo si connette a un punto di accesso Wi-Fi valido, poi si disconnette. L'esempio presuppone inoltre che sul dispositivo sia attivata l'impostazione Dati mobili sempre attivi.
La sequenza temporale è la seguente:
Quando l'app chiama
registerNetworkCallback()
, il callback riceve immediatamente chiamate daonAvailable()
,onNetworkCapabilitiesChanged()
eonLinkPropertiesChanged()
per la rete mobile, perché è l'unica disponibile. Se è disponibile un'altra rete, l'app riceve anche i callback per l'altra rete.
Figura 1. Stato dell'app dopo la chiamata aregisterNetworkCallback()
.Poi, l'app chiama
registerDefaultNetworkCallback()
. Il callback di rete predefinito inizia a ricevere chiamate aonAvailable()
,onNetworkCapabilitiesChanged()
eonLinkPropertiesChanged()
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.
Figura 2. Stato dell'app dopo la registrazione di una rete predefinita.Successivamente, il dispositivo si connette a una rete Wi-Fi (non a consumo). Il callback di rete normale riceve chiamate a
onAvailable()
,onNetworkCapabilitiesChanged()
eonLinkPropertiesChanged()
per la rete Wi-Fi.
Figura 3. Stato dell'app dopo la connessione a una rete Wi-Fi non a consumo.A questo punto, è possibile che la rete Wi-Fi impieghi un po' di tempo per la convalida. In questo caso, le chiamate a
onNetworkCapabilitiesChanged()
per il callback di rete normale non includono la funzionalitàNET_CAPABILITY_VALIDATED
. Dopo un breve periodo di tempo, riceve una chiamata aonNetworkCapabilitiesChanged()
, dove le nuove funzionalità includonoNET_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 di rete predefinito riceve una chiamata a
onAvailable()
,onNetworkCapabilitiesChanged()
eonLinkPropertiesChanged()
per la rete Wi-Fi. La rete mobile passa in background e il callback di rete normale riceve una chiamata aonLosing()
per la rete mobile.Poiché questo esempio presuppone che i dati mobili siano sempre attivi per il dispositivo, la rete mobile non si disconnette mai. Se l'impostazione è disattivata, dopo un po' la rete mobile si disconnette e il callback di rete normale riceve una chiamata a
onLost()
.
Figura 4. Stato dell'app dopo la convalida della rete Wi-Fi.Più tardi, il dispositivo si disconnette improvvisamente dalla rete Wi-Fi perché è uscito dal raggio d'azione. Poiché la rete Wi-Fi si disconnette, il callback di rete normale riceve una chiamata a
onLost()
per la rete Wi-Fi. Poiché la rete mobile è la nuova rete predefinita, il callback di rete predefinito riceve chiamate aonAvailable()
,onNetworkCapabilitiesChanged()
eonLinkPropertiesChanged()
per la rete mobile.
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. In modo
simile, ma con un breve ritardo aggiuntivo per le chiamate a onAvailable()
, anche
il normale callback di rete riceve chiamate a onAvailable()
,
onNetworkCapabilitiesChanged()
e onLinkPropertiesChanged()
perché
la rete mobile 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 altre 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 parametro NetworkCallback
da chiamare quando la rete viene attivata.