O segundo Visualização do Desenvolvedor do Android 11 já está disponível, teste e compartilhe seu feedback.

Gerenciar o uso da rede

Esta lição descreve como desenvolver aplicativos que têm controle preciso do uso dos recursos de rede. Se seu app executar muitas operações de rede, forneça configurações de usuário que permitam controlar os hábitos de dados do app, como a frequência com que o app sincroniza dados, fazer ou não uploads/downloads somente quando estiver no Wi-Fi, usar ou não dados durante roaming, etc. Com esses controles disponíveis, é muito menos provável que os usuários desabilitem o acesso do seu app aos dados em segundo plano quando atingirem o limite, já que podem controlar com precisão a quantidade de dados usados pelo app.

Para saber mais sobre o uso de rede do seu app, incluindo o número e os tipos de conexões de rede durante um período de tempo, leia Apps da Web e Inspecionar o tráfego de rede com o Network Profiler. Para ver diretrizes gerais sobre como escrever apps que minimizem o impacto sobre a duração da bateria gerado por downloads e conexões de rede, consulte Otimizar a duração da bateria e Transferir dados sem consumo de bateria.

Você também pode verificar o exemplo do Network Connect (link em inglês).

Verificar a conexão de rede do dispositivo

Um dispositivo pode ter vários tipos de conexões de rede. Esta lição se concentra no uso de uma conexão de rede Wi-Fi ou rede móvel. Para ver a lista completa dos possíveis tipos de rede, consulte ConnectivityManager.

O Wi-Fi normalmente é mais rápido. Além disso, os dados móveis geralmente são limitados, o que pode fazer com que sejam caros. Uma estratégia comum usada pelos apps é buscar mais dados somente se houver uma rede Wi-Fi disponível.

Antes de executar operações de rede, é recomendável verificar o estado da conectividade da rede. Entre outras coisas, isso pode impedir que seu app use o rádio errado acidentalmente. Se uma conexão de rede não estiver disponível, seu app responderá normalmente. Para verificar a conexão de rede, as seguintes classes são usadas normalmente:

  • ConnectivityManager: responde a consultas sobre o estado da conexão da rede. Também notifica os aplicativos quando há uma alteração da conexão de rede.
  • NetworkInfo: descreve o status da interface de rede de um determinado tipo (atualmente móvel ou Wi-Fi).

O snippet de código a seguir testa a conexão de rede para Wi-Fi e redes móveis. Ele determina se essas interfaces de rede estão disponíveis (ou seja, se a conexão de rede é possível) e/ou conectadas (ou seja, se a conexão de rede existe e se é possível estabelecer soquetes e transmitir dados):

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

As decisões não podem se basear no fato de uma rede estar ou não "disponível". Sempre verifique isConnected() antes de executar operações de rede, já que isConnected() processa casos como redes móveis inconsistentes, modo avião e dados em segundo plano restritos.

Uma maneira mais concisa de verificar se uma interface de rede está disponível é a apresentada a seguir. O método getActiveNetworkInfo() retorna uma instância NetworkInfo, que representa a primeira interface de rede conectada que foi encontrada, ou null se nenhuma das interfaces estiver conectada, o que significa que uma conexão de Internet não está disponível:

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

Para consultar um estado mais preciso, é possível usar NetworkInfo.DetailedState, mas isso raramente será necessário.

Gerenciar o uso da rede

É possível implementar uma atividade de preferências que dá controle explícito ao usuário sobre o uso dos recursos de rede do seu app. Por exemplo:

  • Você pode permitir que os usuários façam upload vídeos somente quando o dispositivo estiver conectado a uma rede Wi-Fi.
  • Você pode sincronizar (ou não), dependendo de critérios específicos, como disponibilidade de rede, intervalo de tempo, etc.

Para criar um app que ofereça compatibilidade com acesso à rede e gerenciamento de uso da rede, seu manifesto precisa ter as permissões e os filtros de intent corretos.

  • O trecho do manifesto abaixo inclui as seguintes permissões:
  • Você pode declarar o filtro de intent para a ação ACTION_MANAGE_NETWORK_USAGE (apresentada no Android 4.0) para indicar que seu aplicativo define uma atividade que oferece opções para controle do uso de dados. ACTION_MANAGE_NETWORK_USAGE mostra configurações para gerenciar o uso de dados de rede de um aplicativo específico. Em casos em que o app tem uma atividade de configurações que permite que os usuários controlem o uso da rede, é necessário declarar esse filtro de intent para essa atividade. No aplicativo de exemplo, essa ação é processada pela classe SettingsActivity, que exibe uma IU de preferências para permitir que os usuários decidam quando fazer o download de um 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>
    

Implementar uma atividade de preferência

Como é possível ver no trecho do manifesto acima, a atividade SettingsActivity do app de exemplo tem um filtro de intent para a ação ACTION_MANAGE_NETWORK_USAGE. SettingsActivity é uma subclasse de PreferenceActivity. Ela exibe uma tela de preferências (apresentada na figura 1) que permite que os usuários especifiquem o seguinte:

  • Se resumos serão exibidos para cada entrada do feed XML ou somente um link para cada entrada.
  • Se será feito o download do feed XML se qualquer conexão de rede estiver disponível ou se somente o Wi-Fi estiver disponível.
Painel de preferências Configurar uma preferência de rede

Figura 1. Atividade de preferências.

Veja a SettingsActivity. Ela implementa OnSharedPreferenceChangeListener. Quando um usuário altera uma preferência, ela lança onSharedPreferenceChanged(), que define refreshDisplay como "true". Isso faz com que a exibição seja atualizada quando o usuário retorna à atividade principal:

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

Responder a alterações de preferências

Quando o usuário altera as preferências na tela de configurações, isso geralmente causa consequências no comportamento do app. Nesse snippet, o app verifica as configurações de preferências em onStart(). Se houver uma correspondência entre a configuração e a conexão de rede do dispositivo (por exemplo, se a configuração for "Wi-Fi" e o dispositivo tiver uma conexão Wi-Fi), o app fará o download do feed e atualizará a tela.

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

    }
    

Detectar alterações de conexão

A última peça do quebra-cabeça é a subclasse BroadcastReceiver, NetworkReceiver. Quando a conexão de rede do dispositivo é alterada, o NetworkReceiver intercepta a ação CONNECTIVITY_ACTION, verifica o status da conexão de rede e define as sinalizações wifiConnected e mobileConnected como "true/false". O resultado é que, na próxima vez que o usuário retornar ao app, o app só fará o download do feed mais recente e atualizará a tela se NetworkActivity.refreshDisplay estiver configurada como true.

Configurar um BroadcastReceiver que é chamado desnecessariamente pode causar uma perda de recursos do sistema. O aplicativo de exemplo registra o BroadcastReceiver NetworkReceiver em onCreate() e cancela o registro em onDestroy(). Isso é mais leve do que declarar um <receiver> no manifesto. Quando um <receiver> é declarado no manifesto, ele pode ativar seu app a qualquer momento, mesmo se ele não tiver sido executado em semanas. Ao registrar e cancelar o registro do NetworkReceiver na atividade principal, você garante que o app não será ativado depois que o usuário sair do app. Se você declarar um <receiver> no manifesto e souber exatamente onde ele é necessário, é possível usar setComponentEnabledSetting() para habilitá-lo e desabilitá-lo conforme necessário.

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