Zarządzanie wykorzystaniem sieci

Z tej lekcji dowiesz się, jak pisać aplikacje o szczegółowej kontroli na wykorzystaniu zasobów sieciowych. Jeśli Twoja aplikacja często operacji sieciowych, należy udostępnić ustawienia użytkownika, które umożliwiają zwyczaje dotyczące danych, np. jak często aplikacja synchronizuje dane, przesyłać/pobierać tylko przez Wi-Fi, czy używać transmisji danych w roamingu, i tak dalej. Mając do dyspozycji te ustawienia, użytkownicy są znacznie mniej skłonni wyłączasz aplikacji dostęp do danych w tle, gdy zbliżają się do limitu, ponieważ mogą zamiast tego dokładnie kontrolować ilość danych zużywanych przez aplikację.

Aby dowiedzieć się więcej o wykorzystaniu sieci przez Twoją aplikację, w tym: typów połączeń sieciowych w danym okresie można znaleźć w artykule Sieć aplikacji i Sprawdzaj ruch w sieci za pomocą sieci program profilujący. Ogólne wskazówki na temat pisać aplikacje, które minimalizują wpływ pobierania plików i sieci na żywotność baterii połączeń, zapoznaj się z artykułem Optymalizowanie czasu pracy na baterii oraz Przenoszenie danych bez obciążania baterii

Możesz też zapoznać się z NetworkConnect przykład.

Sprawdzanie połączenia sieciowego urządzenia

Urządzenie może mieć różne typy połączeń sieciowych. W tej lekcji skupiamy się za pomocą Wi-Fi lub sieci komórkowej. Pełną listę możliwe typy sieci, zobacz ConnectivityManager

Wi-Fi jest zwykle szybsze. Mobilna transmisja danych często jest objęta pomiarem, co pozwala drogie. W przypadku aplikacji często pobierają one duże dane tylko wtedy, gdy sieć Wi-Fi jest dostępna.

Przed rozpoczęciem operacji sieciowych warto sprawdzić stan połączenia sieciowe. Może to między innymi uniemożliwić aplikacji przypadkowo używa niewłaściwego radia. Jeśli połączenie sieciowe jest niedostępne, aplikacja powinna odpowiedzieć płynnie. Aby sprawdzić połączenie sieciowe, zwykle używają następujących klas:

  • ConnectivityManager: odpowiada na pytania o stan sieci z łącznością Google. Powiadomienia są również wysyłane do aplikacji, gdy połączenie sieciowe jest aktywne. zmian.
  • NetworkInfo: opisuje stan elementu interfejsu sieci danego typu (obecnie komórkowe lub Wi-Fi).

Ten fragment kodu służy do testowania połączeń komórkowych przez Wi-Fi i sieć komórkową. Określa czy te interfejsy sieci są dostępne (tzn. czy sieci i/lub podłączone (tzn. czy sieć oraz jeśli jest to możliwe do podłączenia gniazd i przekazywania danych):

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

Pamiętaj, że nie należy podejmować decyzji o tym, czy sieć jest „dostępna”. Ty należy zawsze sprawdzać isConnected() przed podczas wykonywania operacji sieciowych, ponieważ isConnected() obsługuje przypadki takie jak niestabilne sieci komórkowych, trybu samolotowego i ograniczonej transmisji danych w tle.

Bardziej zwięzły sposób na sprawdzenie, czy interfejs sieciowy jest dostępny, to co dalej. Metoda getActiveNetworkInfo() zwraca wystąpienie NetworkInfo reprezentuje pierwszy połączony interfejs sieci, jaki znajdzie, lub null, jeśli żaden z interfejsów nie jest połączony (co oznacza, że połączenie z internetem nie jest połączone dostępne):

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

Aby przesłać bardziej szczegółowe zapytanie, możesz użyć funkcji NetworkInfo.DetailedState ale zazwyczaj nie jest to konieczne.

Zarządzanie wykorzystaniem sieci

Możesz zaimplementować aktywność związaną z preferencjami, która zapewni użytkownikom wyraźną kontrolę nad wykorzystanie zasobów sieciowych przez aplikację. Na przykład:

  • Możesz zezwolić użytkownikom na przesyłanie filmów tylko wtedy, gdy urządzenie jest podłączone do Sieć Wi-Fi.
  • Dane mogą być synchronizowane (lub nie) w zależności od określonych kryteriów, takich jak sieć dostępność, przedział czasu itd.

Aby napisać aplikację, która obsługuje dostęp do sieci i zarządza jej użyciem, plik manifestu musi mieć odpowiednie uprawnienia i filtry intencji.

  • Plik manifestu wyodrębniony w dalszej części tej sekcji zawiera: uprawnienia:
  • Możesz zadeklarować filtr intencji dla funkcji ACTION_MANAGE_NETWORK_USAGE wskazuje, że aplikacja definiuje działanie, które oferuje pozwalają kontrolować użycie danych. ACTION_MANAGE_NETWORK_USAGE pokazuje ustawienia służy do zarządzania wykorzystaniem danych sieciowych przez określoną aplikację. Gdy aplikacja ma aktywność związaną z ustawieniami, która pozwala użytkownikom kontrolować wykorzystanie sieci, zadeklarować ten filtr intencji dla tej aktywności.

W przykładowej aplikacji to działanie jest obsługiwane przez klasę SettingsActivity, który wyświetla interfejs ustawień, aby umożliwić użytkownikom wybór pobierz plik danych.

<?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>

aplikacje, które mają dostęp do poufnych danych użytkownika i są kierowane na Androida 11 i może przyznawać dostęp do sieci dla poszczególnych procesów. Wyraźnie wskazując, który mają dostęp do sieci, izolujesz cały kod, który nie wymaga prześlij dane.

Chociaż aplikacja nie gwarantuje przypadkowego przesyłania danych, pozwala zmniejszyć prawdopodobieństwo wystąpienia błędów w aplikacji powodujących wycieku danych.

Poniżej znajdziesz przykładowy plik manifestu, który korzysta z poszczególnych procesów funkcje:

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

Zaimplementuj aktywność związaną z preferencjami

Jak widać we wcześniejszym fragmencie pliku manifestu, przykładową aplikację aktywność SettingsActivity ma filtr intencji dla: Działanie ACTION_MANAGE_NETWORK_USAGE. SettingsActivity jest podklasą PreferenceActivity it wyświetla ekran ustawień (widoczny na ilustracji 1), na którym użytkownik może określić :

  • Określa, czy wyświetlać podsumowania dla każdego wpisu w kanale XML, czy tylko link dla każdego wpisu.
  • Określa, czy pobrać kanał XML, jeśli dostępne jest połączenie sieciowe, czy tylko wtedy, gdy działa Wi-Fi i dostępności informacji.

Panel ustawień Ustawianie preferencji sieci

Rysunek 1. Aktywność związana z ustawieniami.

Oto SettingsActivity. Zwróć uwagę, że stosuje OnSharedPreferenceChangeListener Gdy użytkownik zmieni preferencje, kliknięcie się uruchomi onSharedPreferenceChanged() co ustawia wartość refreshDisplay na prawda. Powoduje to odświeżenie ekranu, gdy użytkownik powraca do głównej aktywności:

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

Reaguj na zmiany preferencji

Gdy użytkownik zmieni ustawienia na ekranie ustawień, zwykle wpływ na działanie aplikacji. W tym fragmencie aplikacji aplikacja sprawdza ustawienia preferencji w aplikacji onStart(). jeśli występuje dopasowanie między ustawieniem a połączenia sieciowego urządzenia (np. jeśli ustawienie to "Wi-Fi", a parametry urządzenie ma połączenie Wi-Fi), aplikacja pobiera kanał i odświeża wyświetlacz.

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();
        }
    }
...

}

Wykrywanie zmian połączeń

Ostatnim elementem łamigłówki jest podklasa BroadcastReceiver, NetworkReceiver Gdy zmieni się połączenie sieciowe urządzenia, NetworkReceiver przechwytuje akcję CONNECTIVITY_ACTION, określa stan połączenia sieciowego i ustawia flagi wifiConnected i mobileConnected mają odpowiednio wartość prawda/fałsz. Efekt jest taki, przy następnym powrocie użytkownika do aplikacji pobierze ona tylko najnowszy plik danych i zaktualizować sposób wyświetlania, jeśli NetworkActivity.refreshDisplay ma wartość true

Konfiguracja funkcji BroadcastReceiver, która jest niepotrzebnie wywoływana, może być męcząca na zasoby systemowe. Przykładowa aplikacja rejestruje BroadcastReceiver NetworkReceiver in onCreate(), i wyrejestrowuje go onDestroy() To więcej łatwiejsza niż zadeklarowanie elementu <receiver> w pliku manifestu. Gdy zadeklaruj w manifeście plik <receiver>, aplikacja może wybudzić aplikację w dowolnym momencie nawet jeśli nie korzystasz z niej od tygodni. Rejestrowanie i wyrejestrowanie NetworkReceiver w głównej aktywności, dopilnujesz, by aplikacja nie wybudza się po wyjściu przez użytkownika z aplikacji. Jeśli zadeklarujesz <receiver> w pliku manifestu. Wiesz dokładnie, gdzie jest potrzebne, Można użyć setComponentEnabledSetting(). aby odpowiednio go włączyć i wyłączyć.

Oto 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();
        }
    }
}