Gestisci l'utilizzo della rete

Questa lezione descrive come scrivere applicazioni con controllo granulare sull'utilizzo delle risorse di rete. Se la tua applicazione esegue molte operazioni di rete, è necessario fornire impostazioni utente che consentano di controllare le abitudini relative ai dati dell'app, ad esempio la frequenza con cui l'app sincronizza i dati, o meno eseguire caricamenti/download solo in Wi-Fi, anche se utilizzare dati in roaming e così via. Grazie a questi controlli, è molto meno probabile che gli utenti disattivare l'accesso dell'app ai dati in background quando si avvicina al limite. perché possono controllare con precisione la quantità di dati utilizzata dalla tua app.

Per scoprire di più sull'utilizzo della rete della tua app, inclusi il numero e tipi di connessioni di rete in un determinato periodo di tempo, leggi l'articolo Web app e Esaminare il traffico di rete con la rete profiler. Per le linee guida generali su come scrivere app che riducano al minimo l'impatto sulla durata della batteria dei download e della rete connessioni, consulta Ottimizzare la durata della batteria e Trasferisci i dati senza esaurire la batteria.

Puoi anche consultare NetworkConnect esempio.

Controllare la connessione di rete di un dispositivo

Un dispositivo può avere vari tipi di connessioni di rete. Questa lezione è incentrata utilizzando una connessione di rete Wi-Fi o mobile. Per l'elenco completo i possibili tipi di rete, vedi ConnectivityManager

Il Wi-Fi è in genere più veloce. Inoltre, spesso i dati mobili sono a consumo, per cui costoso. Una strategia comune per le app consiste nel recuperare grandi quantità di dati solo se viene fornito un Wi-Fi di rete è disponibile.

Prima di eseguire operazioni di rete, è buona norma controllare lo stato e la connettività di rete. Ciò potrebbe impedire, ad esempio, alla tua app di inavvertitamente l'uso della radio sbagliata. Se una connessione di rete non è disponibile, la tua applicazione deve rispondere correttamente. Per verificare la connessione di rete, in genere utilizzano le seguenti classi:

  • ConnectivityManager: risponde a domande sullo stato della rete e la connettività privata. Inoltre, invia una notifica alle applicazioni quando è attiva la connettività di rete. modifiche.
  • NetworkInfo: descrive lo stato di un interfaccia di rete di un determinato tipo (attualmente Mobile o Wi-Fi).

Questo snippet di codice verifica la connettività di rete per le reti Wi-Fi e mobili. Determina la disponibilità di queste interfacce di rete, ovvero connettività è possibile) e/o connesso (ossia, se la rete connettività esistente e se è possibile stabilire socket e trasmettere dati):

Kotlin

private const val DEBUG_TAG = "NetworkStatusExample"
...
val connMgr = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
var isWifiConn: Boolean = false
var isMobileConn: Boolean = false
connMgr.allNetworks.forEach { network ->
    connMgr.getNetworkInfo(network).apply {
        if (type == ConnectivityManager.TYPE_WIFI) {
            isWifiConn = isWifiConn or isConnected
        }
        if (type == ConnectivityManager.TYPE_MOBILE) {
            isMobileConn = isMobileConn or isConnected
        }
    }
}
Log.d(DEBUG_TAG, "Wifi connected: $isWifiConn")
Log.d(DEBUG_TAG, "Mobile connected: $isMobileConn")

Java

private static final String DEBUG_TAG = "NetworkStatusExample";
...
ConnectivityManager connMgr =
        (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
boolean isWifiConn = false;
boolean isMobileConn = false;
for (Network network : connMgr.getAllNetworks()) {
    NetworkInfo networkInfo = connMgr.getNetworkInfo(network);
    if (networkInfo.getType() == ConnectivityManager.TYPE_WIFI) {
        isWifiConn |= networkInfo.isConnected();
    }
    if (networkInfo.getType() == ConnectivityManager.TYPE_MOBILE) {
        isMobileConn |= networkInfo.isConnected();
    }
}
Log.d(DEBUG_TAG, "Wifi connected: " + isWifiConn);
Log.d(DEBUG_TAG, "Mobile connected: " + isMobileConn);

Tieni presente che non dovresti basare le decisioni sulla "disponibile" o meno di una rete. Tu deve sempre controllare isConnected() prima operazioni di rete in quanto isConnected() gestisce casi come reti mobili, modalità aereo e dati in background con limitazioni.

Un modo più conciso per verificare se un'interfaccia di rete è disponibile è il seguente: . Il metodo getActiveNetworkInfo() restituisce un'istanza NetworkInfo che rappresenta la prima interfaccia di rete connessa che riesce a trovare oppure null se nessuna delle interfacce è connessa (ossia una connessione Internet non è disponibili):

Kotlin

fun isOnline(): Boolean {
    val connMgr = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
    val networkInfo: NetworkInfo? = connMgr.activeNetworkInfo
    return networkInfo?.isConnected == true
}

Java

public boolean isOnline() {
    ConnectivityManager connMgr = (ConnectivityManager)
            getSystemService(Context.CONNECTIVITY_SERVICE);
    NetworkInfo networkInfo = connMgr.getActiveNetworkInfo();
    return (networkInfo != null && networkInfo.isConnected());
}

Per eseguire query in modo più granulare, NetworkInfo.DetailedState, ma questa operazione dovrebbe raramente essere necessaria.

Gestisci l'utilizzo della rete

Puoi implementare un'attività relativa alle preferenze che offra agli utenti un controllo esplicito l'utilizzo delle risorse di rete da parte della tua app. Ad esempio:

  • Potresti consentire agli utenti di caricare video solo quando il dispositivo è connesso a una Rete Wi-Fi.
  • Puoi eseguire la sincronizzazione (o meno) in base a criteri specifici come la rete la disponibilità, l'intervallo di tempo e così via.

Per scrivere un'app che supporti l'accesso alla rete e la gestione dell'utilizzo della rete, il file manifest deve avere le autorizzazioni e i filtri di intent corretti.

  • Il file manifest estratto più avanti in questa sezione include quanto segue: autorizzazioni:
  • Puoi dichiarare il filtro per intent per ACTION_MANAGE_NETWORK_USAGE per indicare che la tua applicazione definisce un'attività che offre opzioni per controllare l'utilizzo dei dati. ACTION_MANAGE_NETWORK_USAGE mostra le impostazioni per gestire l'utilizzo dei dati di rete da una specifica applicazione. Quando l'app ha un'attività relativa alle impostazioni che consente agli utenti di controllare l'utilizzo della rete, dichiarare questo filtro per intent per l'attività in questione.

Nell'applicazione di esempio, questa azione viene gestita dalla classe SettingsActivity, che mostra una UI con preferenze per consentire agli utenti di decidere quando scaricare un feed.

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.android.networkusage"
    ...>

    <uses-sdk android:minSdkVersion="4"
           android:targetSdkVersion="14" />

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

    <application
        ...>
        ...
        <activity android:label="SettingsActivity" android:name=".SettingsActivity">
             <intent-filter>
                <action android:name="android.intent.action.MANAGE_NETWORK_USAGE" />
                <category android:name="android.intent.category.DEFAULT" />
          </intent-filter>
        </activity>
    </application>
</manifest>

App che gestiscono dati utente sensibili e che hanno come target Android 11 e superiore, può concedere l'accesso alla rete per processo. specificando in modo esplicito ai processi è consentito l'accesso alla rete, si deve isolare tutto il codice che non caricare i dati.

Anche se non è garantito che la tua app impedisca il caricamento accidentale di dati, offre un modo per ridurre la possibilità che i bug nella tua app causino un fuga di dati.

Di seguito è riportato un esempio di file manifest che utilizza l'istruzione per processo funzionalità:

<processes>
    <process />
    <deny-permission android:name="android.permission.INTERNET" />
    <process android:process=":withoutnet1" />
    <process android:process="com.android.cts.useprocess.withnet1">
        <allow-permission android:name="android.permission.INTERNET" />
    </process>
    <allow-permission android:name="android.permission.INTERNET" />
    <process android:process=":withoutnet2">
        <deny-permission android:name="android.permission.INTERNET" />
    </process>
    <process android:process="com.android.cts.useprocess.withnet2" />
</processes>

Implementa un'attività relativa alle preferenze

Come puoi vedere nell'estratto del file manifest precedente in questo argomento, l'API di esempio l'attività SettingsActivity ha un filtro per intent per Azione ACTION_MANAGE_NETWORK_USAGE. SettingsActivity è una sottoclasse di PreferenceActivity it mostra una schermata delle preferenze (mostrata nella Figura 1) che consente agli utenti di specificare seguenti:

  • Indica se visualizzare riepiloghi per ogni voce del feed XML o solo un link per ogni voce.
  • Se scaricare il feed XML quando è disponibile una connessione di rete o solo se è disponibile il Wi-Fi disponibili.

Riquadro delle preferenze Impostazione di una preferenza di rete

Figura 1. Attività delle preferenze.

Ecco SettingsActivity. Nota che implementa OnSharedPreferenceChangeListener Quando un utente modifica una preferenza, questa si attiva. onSharedPreferenceChanged(), che imposta refreshDisplay su true. Questo fa sì che il display si aggiorni quando l'utente torna all'attività principale:

Kotlin

class SettingsActivity : PreferenceActivity(), OnSharedPreferenceChangeListener {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        // Loads the XML preferences file
        addPreferencesFromResource(R.xml.preferences)
    }

    override fun onResume() {
        super.onResume()

        // Registers a listener whenever a key changes
        preferenceScreen?.sharedPreferences?.registerOnSharedPreferenceChangeListener(this)
    }

    override fun onPause() {
        super.onPause()

        // Unregisters the listener set in onResume().
        // It's best practice to unregister listeners when your app isn't using them to cut down on
        // unnecessary system overhead. You do this in onPause().
        preferenceScreen?.sharedPreferences?.unregisterOnSharedPreferenceChangeListener(this)
    }

    // When the user changes the preferences selection,
    // onSharedPreferenceChanged() restarts the main activity as a new
    // task. Sets the refreshDisplay flag to "true" to indicate that
    // the main activity should update its display.
    // The main activity queries the PreferenceManager to get the latest settings.

    override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String) {
        // Sets refreshDisplay to true so that when the user returns to the main
        // activity, the display refreshes to reflect the new settings.
        NetworkActivity.refreshDisplay = true
    }
}

Java

public class SettingsActivity extends PreferenceActivity implements OnSharedPreferenceChangeListener {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // Loads the XML preferences file
        addPreferencesFromResource(R.xml.preferences);
    }

    @Override
    protected void onResume() {
        super.onResume();

        // Registers a listener whenever a key changes
        getPreferenceScreen().getSharedPreferences().registerOnSharedPreferenceChangeListener(this);
    }

    @Override
    protected void onPause() {
        super.onPause();

       // Unregisters the listener set in onResume().
       // It's best practice to unregister listeners when your app isn't using them to cut down on
       // unnecessary system overhead. You do this in onPause().
       getPreferenceScreen().getSharedPreferences().unregisterOnSharedPreferenceChangeListener(this);
    }

    // When the user changes the preferences selection,
    // onSharedPreferenceChanged() restarts the main activity as a new
    // task. Sets the refreshDisplay flag to "true" to indicate that
    // the main activity should update its display.
    // The main activity queries the PreferenceManager to get the latest settings.

    @Override
    public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
        // Sets refreshDisplay to true so that when the user returns to the main
        // activity, the display refreshes to reflect the new settings.
        NetworkActivity.refreshDisplay = true;
    }
}

Rispondere alle modifiche delle preferenze

Quando l'utente modifica le preferenze nella schermata delle impostazioni, in genere: conseguenze sul comportamento dell'app. In questo snippet, l'app controlla impostazioni delle preferenze in onStart(). in caso di corrispondenza tra l'impostazione e connessione di rete del dispositivo (ad esempio, se l'impostazione è "Wi-Fi" e i dispositivo dispone di una connessione Wi-Fi), l'app scarica il feed e aggiorna display.

Kotlin

class NetworkActivity : Activity() {

    // The BroadcastReceiver that tracks network connectivity changes.
    private lateinit var receiver: NetworkReceiver

    public override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        // Registers BroadcastReceiver to track network connection changes.
        val filter = IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION)
        receiver = NetworkReceiver()
        this.registerReceiver(receiver, filter)
    }

    public override fun onDestroy() {
        super.onDestroy()
        // Unregisters BroadcastReceiver when app is destroyed.
        this.unregisterReceiver(receiver)
    }

    // Refreshes the display if the network connection and the
    // pref settings allow it.

    public override fun onStart() {
        super.onStart()

        // Gets the user's network preference settings
        val sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this)

        // Retrieves a string value for the preferences. The second parameter
        // is the default value to use if a preference value is not found.
        sPref = sharedPrefs.getString("listPref", "Wi-Fi")

        updateConnectedFlags()

        if (refreshDisplay) {
            loadPage()
        }
    }

    // Checks the network connection and sets the wifiConnected and mobileConnected
    // variables accordingly.
    fun updateConnectedFlags() {
        val connMgr = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager

        val activeInfo: NetworkInfo? = connMgr.activeNetworkInfo
        if (activeInfo?.isConnected == true) {
            wifiConnected = activeInfo.type == ConnectivityManager.TYPE_WIFI
            mobileConnected = activeInfo.type == ConnectivityManager.TYPE_MOBILE
        } else {
            wifiConnected = false
            mobileConnected = false
        }
    }

    // Uses AsyncTask subclass to download the XML feed from stackoverflow.com.
    fun loadPage() {
        if (sPref == ANY && (wifiConnected || mobileConnected) || sPref == WIFI && wifiConnected) {
            // AsyncTask subclass
            DownloadXmlTask().execute(URL)
        } else {
            showErrorPage()
        }
    }

    companion object {

        const val WIFI = "Wi-Fi"
        const val ANY = "Any"
        const val SO_URL = "http://stackoverflow.com/feeds/tag?tagnames=android&sort;=newest"

        // Whether there is a Wi-Fi connection.
        private var wifiConnected = false
        // Whether there is a mobile connection.
        private var mobileConnected = false
        // Whether the display should be refreshed.
        var refreshDisplay = true

        // The user's current network preference setting.
        var sPref: String? = null
    }
...

}

Java

public class NetworkActivity extends Activity {
    public static final String WIFI = "Wi-Fi";
    public static final String ANY = "Any";
    private static final String URL = "http://stackoverflow.com/feeds/tag?tagnames=android&sort;=newest";

    // Whether there is a Wi-Fi connection.
    private static boolean wifiConnected = false;
    // Whether there is a mobile connection.
    private static boolean mobileConnected = false;
    // Whether the display should be refreshed.
    public static boolean refreshDisplay = true;

    // The user's current network preference setting.
    public static String sPref = null;

    // The BroadcastReceiver that tracks network connectivity changes.
    private NetworkReceiver receiver = new NetworkReceiver();

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // Registers BroadcastReceiver to track network connection changes.
        IntentFilter filter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
        receiver = new NetworkReceiver();
        this.registerReceiver(receiver, filter);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        // Unregisters BroadcastReceiver when app is destroyed.
        if (receiver != null) {
            this.unregisterReceiver(receiver);
        }
    }

    // Refreshes the display if the network connection and the
    // pref settings allow it.

    @Override
    public void onStart () {
        super.onStart();

        // Gets the user's network preference settings
        SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this);

        // Retrieves a string value for the preferences. The second parameter
        // is the default value to use if a preference value is not found.
        sPref = sharedPrefs.getString("listPref", "Wi-Fi");

        updateConnectedFlags();

        if(refreshDisplay){
            loadPage();
        }
    }

    // Checks the network connection and sets the wifiConnected and mobileConnected
    // variables accordingly.
    public void updateConnectedFlags() {
        ConnectivityManager connMgr = (ConnectivityManager)
                getSystemService(Context.CONNECTIVITY_SERVICE);

        NetworkInfo activeInfo = connMgr.getActiveNetworkInfo();
        if (activeInfo != null && activeInfo.isConnected()) {
            wifiConnected = activeInfo.getType() == ConnectivityManager.TYPE_WIFI;
            mobileConnected = activeInfo.getType() == ConnectivityManager.TYPE_MOBILE;
        } else {
            wifiConnected = false;
            mobileConnected = false;
        }
    }

    // Uses AsyncTask subclass to download the XML feed from stackoverflow.com.
    public void loadPage() {
        if (((sPref.equals(ANY)) && (wifiConnected || mobileConnected))
                || ((sPref.equals(WIFI)) && (wifiConnected))) {
            // AsyncTask subclass
            new DownloadXmlTask().execute(URL);
        } else {
            showErrorPage();
        }
    }
...

}

Rileva le modifiche alla connessione

L'ultimo pezzo del puzzle è BroadcastReceiver, NetworkReceiver. Quando la connessione di rete del dispositivo cambia, NetworkReceiver intercetta l'azione CONNECTIVITY_ACTION, determina lo stato della connessione di rete e imposta i flag wifiConnected e mobileConnected su true/false di conseguenza. Il risultato è che la prossima volta che l'utente torna all'app, quest'ultima scaricherà solo l'ultimo feed e aggiorna la visualizzazione se NetworkActivity.refreshDisplay è impostato su true.

Configurare un BroadcastReceiver che viene chiamato inutilmente può essere un problema sulle risorse di sistema. L'applicazione di esempio registra il BroadcastReceiver NetworkReceiver pollici onCreate(), e ne annulla la registrazione onDestroy(). Questo è altro rispetto alla dichiarazione di un valore <receiver> nel file manifest. Quando dichiarare <receiver> nel file manifest, può riattivare la tua app in qualsiasi momento anche se non lo utilizzi da settimane. Registrando e annullando la registrazione NetworkReceiver nell'attività principale, ti assicuri che l'app non essere svegliato dopo che l'utente ha abbandonato l'app. Se dichiari un <receiver> nel file manifest e sai esattamente dove ti serve, è possibile usare setComponentEnabledSetting(): per abilitarla e disabilitarla in base alle necessità.

Ecco NetworkReceiver:

Kotlin

class NetworkReceiver : BroadcastReceiver() {

    override fun onReceive(context: Context, intent: Intent) {
        val conn = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
        val networkInfo: NetworkInfo? = conn.activeNetworkInfo

        // Checks the user prefs and the network connection. Based on the result, decides whether
        // to refresh the display or keep the current display.
        // If the userpref is Wi-Fi only, checks to see if the device has a Wi-Fi connection.
        if (WIFI == sPref && networkInfo?.type == ConnectivityManager.TYPE_WIFI) {
            // If device has its Wi-Fi connection, sets refreshDisplay
            // to true. This causes the display to be refreshed when the user
            // returns to the app.
            refreshDisplay = true
            Toast.makeText(context, R.string.wifi_connected, Toast.LENGTH_SHORT).show()

            // If the setting is ANY network and there is a network connection
            // (which by process of elimination would be mobile), sets refreshDisplay to true.
        } else if (ANY == sPref && networkInfo != null) {
            refreshDisplay = true

            // Otherwise, the app can't download content--either because there is no network
            // connection (mobile or Wi-Fi), or because the pref setting is WIFI, and there
            // is no Wi-Fi connection.
            // Sets refreshDisplay to false.
        } else {
            refreshDisplay = false
            Toast.makeText(context, R.string.lost_connection, Toast.LENGTH_SHORT).show()
        }
    }
}

Java

public class NetworkReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        ConnectivityManager conn =  (ConnectivityManager)
            context.getSystemService(Context.CONNECTIVITY_SERVICE);
        NetworkInfo networkInfo = conn.getActiveNetworkInfo();

        // Checks the user prefs and the network connection. Based on the result, decides whether
        // to refresh the display or keep the current display.
        // If the userpref is Wi-Fi only, checks to see if the device has a Wi-Fi connection.
        if (WIFI.equals(sPref) && networkInfo != null
            && networkInfo.getType() == ConnectivityManager.TYPE_WIFI) {
            // If device has its Wi-Fi connection, sets refreshDisplay
            // to true. This causes the display to be refreshed when the user
            // returns to the app.
            refreshDisplay = true;
            Toast.makeText(context, R.string.wifi_connected, Toast.LENGTH_SHORT).show();

        // If the setting is ANY network and there is a network connection
        // (which by process of elimination would be mobile), sets refreshDisplay to true.
        } else if (ANY.equals(sPref) && networkInfo != null) {
            refreshDisplay = true;

        // Otherwise, the app can't download content--either because there is no network
        // connection (mobile or Wi-Fi), or because the pref setting is WIFI, and there
        // is no Wi-Fi connection.
        // Sets refreshDisplay to false.
        } else {
            refreshDisplay = false;
            Toast.makeText(context, R.string.lost_connection, Toast.LENGTH_SHORT).show();
        }
    }
}