Android consente alle app di rilevare le variazioni dinamiche della 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 associare socket alla rete. Quando la rete si disconnette, l'oggettoNetwork
non è più utilizzabile. Anche se in seguito il dispositivo si ricollega alla stessa appliance, un nuovo oggettoNetwork
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 i route di rete installati 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 query sull'oggetto per determinare se la rete è in grado di inviare MMS, è dietro un captive portal o è soggetta a misurazione.
Le app interessate allo stato immediato della connettività in un determinato momento possono chiamare i metodi ConnectivityManager
per scoprire che tipo di rete è disponibile. Questi metodi sono utili per il debug e per esaminare di tanto in tanto un'istantanea della connettività disponibile in un determinato momento.
Tuttavia, i metodi ConnectivityManager
sincroni non comunicano all'app ciò che accade dopo una chiamata, pertanto non ti consentono di aggiornare l'interfaccia utente. Inoltre, non possono modificare il comportamento delle app in base alla disconnessione della rete o alla variazione delle funzionalità della rete.
La connettività può cambiare in qualsiasi momento e la maggior parte delle app deve avere una visualizzazione sempre aggiornata dello stato della rete 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
variazione pertinente della connettività, senza dover ricorrere a polling costosi
che potrebbero perdere aggiornamenti rapidi.
L'utilizzo di NetworkCallback
e di altri modi per conoscere lo stato di connettività del dispositivo non richiede alcuna autorizzazione particolare.
Tuttavia, alcune emittenti sono soggette a autorizzazioni specifiche.
Ad esempio, potrebbero essere presenti reti con limitazioni non disponibili per le app. Il collegamento a una rete in background richiede l'autorizzazione CHANGE_NETWORK_STATE
. Inoltre, alcune chiamate potrebbero richiedere autorizzazioni specifiche per essere eseguite. Per maggiori dettagli, consulta la documentazione specifica di ogni chiamata.
Ottenere lo 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 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 noti del sistema relativi a 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 di cui hai bisogno.
L'oggetto NetworkCapabilities
incapsula le informazioni sui protocolli di trasporto 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 rete mobile.
Anche le VPN e il Wi-Fi peer-to-peer possono essere considerati trasporti.
Su Android, una rete può avere più trasporti contemporaneamente. Un esempio è una VPN che opera sia su reti Wi-Fi che mobili. La VPN ha i metodi di trasporto Wi-Fi, mobile e VPN. Per scoprire se una rete ha un determinato mezzo di trasporto, utilizza il metodo NetworkCapabilities.hasTransport(int)
con una delle costanti NetworkCapabilities.TRANSPORT_*
.
Una funzionalità descrive una proprietà della rete. Ecco alcuni esempi di funzionalità:MMS
, NOT_METERED
e INTERNET
. Una rete con la funzionalità MMS può inviare
e ricevere messaggi di questo tipo, mentre una rete senza questa
funzionalità non può. Una rete con la funzionalità NOT_METERED
non addebita all'utente i dati. L'app può verificare la presenza delle 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 reale capacità 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 di solito ha la funzionalità
INTERNET
, mentre una rete Wi-Fi P2P locale in genere no. Per la connettività effettiva, consultaNET_CAPABILITY_VALIDATED
.NET_CAPABILITY_NOT_METERED
: indica che la rete non è a consumo. Una rete viene classificata come conteggiata 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 all'internet pubblico quando viene sottoposta a test. Una rete dietro un portale captive o una rete che non fornisce la risoluzione dei nomi di dominio non ha questa funzionalità. Si tratta della valutazione più accurata 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 interruzioni della 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 test.
Esistono altre funzionalità che potrebbero interessare 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 portale di acquisizione, mostra una notifica che invita l'utente a eseguire l'accesso. Mentre questo avviene, la rete dispone delle funzionalità NET_CAPABILITY_INTERNET
e NET_CAPABILITY_CAPTIVE_PORTAL
, ma non della funzionalità NET_CAPABILITY_VALIDATED
.
Quando l'utente esegue un'azione e accede alla pagina del captive portal, il dispositivo diventa in grado di 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 attivata, ad esempio passare dalla rete mobile a quella Wi-Fi per la rete di base. In questo caso, la rete perde il trasporto TRANSPORT_CELLULAR
e acquisisce il trasporto TRANSPORT_WIFI
, mantenendo al contempo 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, determinata dal sistema. In genere, il sistema preferisce le reti senza limiti di traffico a quelle con limiti 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 altre reti.
La rete impostata come predefinita può cambiare in qualsiasi momento durante il ciclo di vita di un'app. Un esempio tipico è il dispositivo che entra nel raggio d'azione di un punto di accesso Wi-Fi noto, attivo, senza limiti di velocità e più veloce del mobile. Il dispositivo si connette a questo punto di accesso e imposta la rete predefinita per tutte le app sulla nuova rete Wi-Fi.
Quando una nuova rete diventa predefinita, qualsiasi nuova connessione aperta dall'app utilizza questa rete. A un certo punto, tutte le connessioni rimanenti sulla rete predefinita precedente vengono interrotte forzatamente. Se è importante per l'app sapere 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 predefinita, l'app riceve una chiamata per la nuova rete.onAvailable(Network)
Implementa
onCapabilitiesChanged(Network,NetworkCapabilities)
,
onLinkPropertiesChanged(Network,LinkProperties)
o entrambi per reagire in modo appropriato alle variazioni della connettività.
Per un callback registrato con registerDefaultNetworkCallback()
, onLost()
indica che la rete ha perso lo stato di rete predefinita. Potrebbe essere disconnesso.
Sebbene sia possibile conoscere i trasporti utilizzati dalla rete predefinita eseguendo una query su NetworkCapabilities.hasTransport(int)
, questo non è un buon proxy per la larghezza di banda o la misurazione della rete. L'app non può assumere che la rete Wi-Fi sia sempre senza limiti 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
NET_CAPABILITY_NOT_METERED
argomenti per determinare la misurazione. 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 eseguire altri compiti, chiamali in un thread di lavoro separato utilizzando la variante ConnectivityManager.registerDefaultNetworkCallback(NetworkCallback, Handler)
.
Annullare la registrazione del tuo numero di richiamata quando non ti serve più chiamando
ConnectivityManager.unregisterNetworkCallback(NetworkCallback)
.
onPause()
della tua attività principale è un buon punto di partenza, soprattutto se registri il riavvio in onResume()
.
Altre emittenti
Sebbene la rete predefinita sia l'unica pertinente per la maggior parte delle app, alcune
app
potrebbero essere interessate ad altre reti disponibili. Per scoprirli, le app creano un NetworkRequest
adatto 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 esserci un'unica rete predefinita applicata a un'app, questa versione consente all'app di vedere contemporaneamente tutte le emittenti disponibili, pertanto una chiamata a onLost(Network)
significa che la rete si è disconnessa definitivamente, non che non sia più predefinita.
L'app crea un NetworkRequest
per informare ConnectivityManager
del tipo di emittenti che vuole ascoltare. L'esempio seguente mostra come creare un
NetworkRequest
per un'app interessata solo alle connessioni internet senza misurazione:
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 senza misurazione 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
app.
Chiamare
ConnectivityManager.unregisterNetworkCallback(NetworkCallback)
quando la richiamata 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 l'app, controlla i valori predefiniti per verificare se corrispondono al tuo caso d'uso e cancellali se vuoi che l'app riceva una notifica sulle reti che non dispongono di queste funzionalità. D'altra parte, aggiungi funzionalità per evitare di ricevere chiamate 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
al 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 callback 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 con connettività mobile. In questo esempio, il dispositivo si connette a un punto di accesso Wi-Fi di buona qualità, quindi si disconnette. L'esempio presuppone inoltre che sul dispositivo sia attiva 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 rete 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()
.L'app chiama quindi
registerDefaultNetworkCallback()
. Il callback della rete predefinita inizia a ricevere chiamate aonAvailable()
,onNetworkCapabilitiesChanged()
eonLinkPropertiesChanged()
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.
Figura 2. Stato dell'app dopo la registrazione di una rete predefinita.In un secondo momento, il dispositivo si connette a una rete Wi-Fi (senza misurazione). 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 senza limiti di traffico.A questo punto, è possibile che la convalida della rete Wi-Fi richieda del tempo. In questo caso, le chiamate
onNetworkCapabilitiesChanged()
per il callback della rete normale non includono la funzionalitàNET_CAPABILITY_VALIDATED
. Dopo breve 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 è misurata. La rete Wi-Fi diventa la rete predefinita, quindi il callback della rete predefinita riceve una chiamata a
onAvailable()
,onNetworkCapabilitiesChanged()
eonLinkPropertiesChanged()
per la rete Wi-Fi. La rete mobile passa in background e il callback della rete normale riceve una chiamata al numeroonLosing()
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' di tempo 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 dall'area di copertura. Poiché il Wi-Fi si disconnette, il callback di rete normale riceve una chiamata a
onLost()
per il Wi-Fi. Poiché la rete mobile è la nuova rete predefinita, il callback della rete predefinita 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 il Wi-Fi si disconnette il dispositivo tenta di riconnettersi a una rete mobile. La situazione è simile, ma con un breve ritardo aggiuntivo per le chiamate onAvailable()
. Inoltre, il richiamata della rete normale riceve anche le chiamate a onAvailable()
, onNetworkCapabilitiesChanged()
e onLinkPropertiesChanged()
perché il servizio mobile diventa disponibile.
Restrizioni all'utilizzo della rete per il trasferimento di dati
Il fatto che tu possa 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, consulta
NET_CAPABILITY_INTERNET
e
NET_CAPABILITY_VALIDATED
.
Anche l'utilizzo delle reti in background è soggetto 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 provare a 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 viene attivata la rete.