Просмотр состояния сети

Android позволяет приложениям узнавать о динамических изменениях в подключении. Чтобы отслеживать изменения в подключении и реагировать на них, используйте следующие классы:

  • ConnectivityManager сообщает приложению о состоянии подключения в системе.
  • Класс Network представляет одну из сетей, к которым подключено устройство. Объект Network можно использовать в качестве ключа для сбора информации о сети с помощью ConnectivityManager или для привязки сокетов в сети. Когда сеть отключается, объект Network перестает быть доступным. Даже если устройство позже повторно подключится к тому же устройству, новая сеть будет представлена новым объектом Network.
  • Объект LinkProperties содержит информацию о ссылке для сети, например список DNS-серверов, локальные IP-адреса и сетевые маршруты, установленные для сети.
  • Объект NetworkCapabilities содержит информацию о свойствах сети, например о транспорте (Wi-Fi, мобильная сеть, Bluetooth) и возможностях сети. Например, вы можете запросить объект, чтобы определить, способна ли сеть отправлять MMS, находится ли она за страницей входа или является ли она лимитной.

Приложения, которым требуется информация о текущем состоянии подключения, могут вызывать методы ConnectivityManager, чтобы узнать, какой тип сети доступен. Эти методы полезны для отладки и периодического просмотра снимка доступных подключений в любой момент времени.

Однако синхронные методы ConnectivityManager не сообщают приложению о том, что происходит после вызова, поэтому они не позволяют обновлять пользовательский интерфейс. Они также не могут корректировать поведение приложения в зависимости от отключения сети или изменения ее возможностей.

Подключение может измениться в любой момент, и большинству приложений необходимо иметь всегда свежее и актуальное представление о состоянии сети на устройстве. Приложения могут зарегистрировать обратный вызов с помощью ConnectivityManager, чтобы получать оповещения об изменениях. Используя обратный вызов, ваше приложение может немедленно реагировать на любые соответствующие изменения в подключении, не прибегая к дорогостоящему опросу, который может пропустить быстрые обновления.

Для использования NetworkCallback и других способов определения состояния подключения устройства не требуется специальное разрешение. Однако для некоторых сетей действуют особые разрешения. Например, некоторые сети могут быть недоступны для приложений. Для привязки к фоновой сети требуется разрешение CHANGE_NETWORK_STATE. А для выполнения некоторых вызовов могут потребоваться определенные разрешения. Подробную информацию можно найти в документации по каждому вызову.

Получение мгновенного статуса

Устройство Android может поддерживать несколько подключений одновременно. Чтобы получить информацию о текущем состоянии сети, сначала получите экземпляр ConnectivityManager:

Kotlin

val connectivityManager = getSystemService(ConnectivityManager::class.java)

Java

ConnectivityManager connectivityManager = getSystemService(ConnectivityManager.class);

Затем используйте этот экземпляр, чтобы получить ссылку на текущую сеть по умолчанию для вашего приложения:

Kotlin

val currentNetwork = connectivityManager.getActiveNetwork()

Java

Network currentNetwork = connectivityManager.getActiveNetwork();

Получив ссылку на сеть, приложение может запросить информацию о ней:

Kotlin

val caps = connectivityManager.getNetworkCapabilities(currentNetwork)
val linkProperties = connectivityManager.getLinkProperties(currentNetwork)

Java

NetworkCapabilities caps = connectivityManager.getNetworkCapabilities(currentNetwork);
LinkProperties linkProperties = connectivityManager.getLinkProperties(currentNetwork);

Чтобы получить доступ к дополнительным функциям, зарегистрируйте NetworkCallback. Подробнее о том, как регистрировать обратные вызовы, читайте в разделе Прослушивание сетевых событий.

NetworkCapabilities и LinkProperties

Объекты NetworkCapabilities и LinkProperties предоставляют информацию обо всех атрибутах, которые система знает о сети.

Объект LinkProperties содержит информацию о маршрутах, адресах ссылок, названии интерфейса, прокси-сервере (если он есть) и DNS-серверах. Вызовите нужный метод объекта LinkProperties, чтобы получить необходимую информацию.

Объект NetworkCapabilities содержит информацию о транспортах сети и их возможностях.

Транспорт – это абстракция физической среды, в которой работает сеть. Примеры: Ethernet, Wi-Fi и мобильная сеть. VPN и одноранговые сети Wi-Fi также могут быть транспортными сетями. В Android сеть может одновременно использовать несколько транспортов. Примером этого является VPN, работающая как в сетях Wi-Fi, так и в мобильных сетях. VPN использует транспортные уровни Wi-Fi, мобильной сети и VPN. Чтобы узнать, есть ли в сети определенный транспорт, используйте метод NetworkCapabilities.hasTransport(int) с одной из констант NetworkCapabilities.TRANSPORT_*.

Возможность описывает свойство сети. Примеры возможностей: MMS, NOT_METERED и INTERNET. Сеть с поддержкой MMS может отправлять и получать мультимедийные сообщения, а сеть без такой поддержки – нет. Сеть с возможностью NOT_METERED не взимает с пользователя плату за передачу данных. Приложение может проверить наличие необходимых возможностей с помощью метода NetworkCapabilities.hasCapability(int) и одной из констант NetworkCapabilities.NET_CAPABILITY_*.

Наиболее полезные константы NET_CAPABILITY_*:

  • NET_CAPABILITY_INTERNET – сеть настроена для доступа к интернету. Это касается настройки, а не фактической возможности доступа к общедоступным серверам. Например, сеть может быть настроена для доступа к интернету, но при этом иметь страницу входа.

    Мобильная сеть оператора обычно поддерживает INTERNET, а локальная одноранговая сеть Wi-Fi – нет. Фактические данные о подключении можно посмотреть в разделе NET_CAPABILITY_VALIDATED.

  • NET_CAPABILITY_NOT_METERED – сеть без тарификации. Сеть классифицируется как лимитная, если пользователь чувствителен к интенсивному использованию данных в этом подключении из-за денежных затрат, ограничений на передачу данных или проблем с производительностью батареи.

  • NET_CAPABILITY_NOT_VPN – сеть не является виртуальной частной сетью.

  • NET_CAPABILITY_VALIDATED – указывает, что сеть предоставляет доступ к интернету. Сети, использующие страницу входа или не поддерживающие преобразование доменных имен, не имеют такой возможности. Это наиболее точный способ определить, предоставляет ли сеть доступ к интернету. Однако даже в проверенной сети может быть настроена фильтрация по IP-адресу или могут возникать внезапные потери подключения, например из-за слабого сигнала.

  • NET_CAPABILITY_CAPTIVE_PORTAL – указывает, что в сети есть страница входа.

Есть и другие возможности, которые могут быть интересны для более специализированных приложений. Подробную информацию вы найдете в определениях параметров в NetworkCapabilities.hasCapability(int).

Возможности сети могут измениться в любой момент. Когда система обнаруживает страницу входа, она показывает уведомление с предложением войти. Пока идет этот процесс, сеть имеет возможности NET_CAPABILITY_INTERNET и NET_CAPABILITY_CAPTIVE_PORTAL, но не NET_CAPABILITY_VALIDATED.

Когда пользователь выполняет вход на странице входа, устройство получает доступ к интернету, а сеть приобретает возможность NET_CAPABILITY_VALIDATED и теряет возможность NET_CAPABILITY_CAPTIVE_PORTAL.

Аналогичным образом могут динамически меняться и транспортные средства сети. Например, VPN может перенастроиться на использование более быстрой сети, которая только что появилась, например переключиться с мобильной сети на Wi-Fi. В этом случае сеть теряет транспорт TRANSPORT_CELLULAR и получает транспорт TRANSPORT_WIFI, сохраняя транспорт TRANSPORT_VPN.

Как отслеживать сетевые события

Чтобы получать информацию о событиях в сети, используйте класс NetworkCallback вместе с ConnectivityManager.registerDefaultNetworkCallback(NetworkCallback) и ConnectivityManager.registerNetworkCallback(NetworkCallback). Эти два метода служат разным целям.

У всех приложений Android есть сеть по умолчанию, которая определяется системой. Обычно система отдает предпочтение сетям без тарификации трафика и более быстрым сетям.

Когда приложение отправляет сетевой запрос, например с помощью HttpsURLConnection, система выполняет его, используя сеть по умолчанию. Приложения могут отправлять трафик и по другим сетям. Подробнее о дополнительных сетях…

Сеть, заданная по умолчанию, может измениться в любой момент в течение жизненного цикла приложения. Типичный пример – устройство оказывается в зоне действия известной, активной, безлимитной и более быстрой, чем мобильная, точки доступа Wi-Fi. Устройство подключается к этой точке доступа и переключает сеть по умолчанию для всех приложений на новую сеть Wi-Fi.

Когда новая сеть становится сетью по умолчанию, все новые подключения, которые открывает приложение, используют эту сеть. Через некоторое время все оставшиеся подключения к предыдущей сети по умолчанию принудительно завершаются. Если приложению важно знать, когда меняется сеть по умолчанию, оно регистрирует обратный вызов сети по умолчанию следующим образом:

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

Когда сеть по умолчанию меняется, приложение получает вызов onAvailable(Network) для новой сети. Чтобы приложение правильно реагировало на изменения в подключении, реализуйте onCapabilitiesChanged(Network,NetworkCapabilities), onLinkPropertiesChanged(Network,LinkProperties) или оба этих метода.

Для обратного вызова, зарегистрированного с помощью registerDefaultNetworkCallback(), onLost() означает, что сеть потеряла статус сети по умолчанию. Возможно, оно отключено.

Хотя вы можете узнать о транспортах, которые использует сеть по умолчанию, запросив NetworkCapabilities.hasTransport(int), это плохой прокси-сервер для пропускной способности или лимитированности сети. Приложение не должно предполагать, что Wi-Fi всегда безлимитный и всегда обеспечивает лучшую пропускную способность, чем мобильная связь.

Вместо этого используйте NetworkCapabilities.getLinkDownstreamBandwidthKbps() для измерения пропускной способности и NetworkCapabilites.hasCapability(int) с аргументами NET_CAPABILITY_NOT_METERED для определения лимита. Дополнительную информацию можно найти в разделе NetworkCapabilities и LinkProperties.

По умолчанию методы обратного вызова вызываются в потоке подключения приложения, который используется ConnectivityManager. Если для реализации обратных вызовов требуется больше времени, вызовите их в отдельном рабочем потоке, используя вариант ConnectivityManager.registerDefaultNetworkCallback(NetworkCallback, Handler).

Если обратный вызов больше не нужен, отмените его регистрацию с помощью метода ConnectivityManager.unregisterNetworkCallback(NetworkCallback). Для этого хорошо подходит метод onPause() основного действия, особенно если вы регистрируете обратный вызов в onResume().

Дополнительные сети (для продвинутых пользователей)

Хотя для большинства приложений подходит только сеть по умолчанию, некоторые приложения могут использовать и другие доступные сети. Чтобы узнать, какие приложения могут обрабатывать такие намерения, создайте объект NetworkRequest, соответствующий их потребностям, и вызовите ConnectivityManager.registerNetworkCallback(NetworkRequest, NetworkCallback).

Процесс аналогичен прослушиванию сети по умолчанию. Однако, хотя в любой момент времени к приложению может применяться только одна сеть по умолчанию, эта версия позволяет приложению видеть все доступные сети одновременно, поэтому вызов onLost(Network) означает, что сеть отключена навсегда, а не то, что она больше не является сетью по умолчанию.

Приложение создает объект NetworkRequest, чтобы сообщить ConnectivityManager, какие сети его интересуют. В следующем примере показано, как создать NetworkRequest для приложения, которому требуется только безлимитное подключение к интернету:

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

Это означает, что ваше приложение будет получать информацию обо всех изменениях, касающихся любой безлимитной сети в системе.

Что касается обратного вызова сети по умолчанию, существует версия registerNetworkCallback(NetworkRequest, NetworkCallback, Handler), которая принимает Handler, поэтому она не загружает поток Connectivity вашего приложения.

Вызовите ConnectivityManager.unregisterNetworkCallback(NetworkCallback), когда обратный вызов больше не нужен. Приложение может одновременно регистрировать несколько обратных вызовов сети.

Для удобства объект NetworkRequest содержит общие возможности, необходимые большинству приложений, в том числе:

При написании приложения проверьте, соответствуют ли значения по умолчанию вашему сценарию использования, и очистите их, если хотите, чтобы приложение получало уведомления о сетях, у которых нет этих возможностей. С другой стороны, добавьте возможности, чтобы избежать вызова при любом изменении подключения в сетях, с которыми ваше приложение не взаимодействует.

Например, если приложению нужно отправлять MMS-сообщения, добавьте NET_CAPABILITY_MMS в NetworkRequest, чтобы не получать уведомления обо всех сетях, которые не могут отправлять MMS-сообщения. Добавьте TRANSPORT_WIFI_AWARE, если вашему приложению требуется только одноранговое подключение по Wi-Fi. NET_CAPABILITY_INTERNET и NET_CAPABILITY_VALIDATED полезны, если вы хотите передавать данные на сервер в интернете.

Пример последовательности обратных вызовов

В этом разделе описывается последовательность обратных вызовов, которые может получить приложение, если оно регистрирует как обратный вызов по умолчанию, так и обычный обратный вызов на устройстве с мобильным подключением. В этом примере устройство подключается к точке доступа Wi-Fi с хорошим сигналом, а затем отключается от нее. В примере также предполагается, что на устройстве включен параметр Мобильный интернет всегда включен.

Вот хронология событий:

  1. Когда приложение вызывает registerNetworkCallback(), обратный вызов немедленно получает вызовы от onAvailable(), onNetworkCapabilitiesChanged() и onLinkPropertiesChanged() для мобильной сети, поскольку доступна только эта сеть. Если доступна другая сеть, приложение также получает обратные вызовы для нее.

    Диаграмма состояний, на которой показано событие обратного вызова для регистрации сети и обратные вызовы, активируемые этим событием
    Рисунок 1. Состояние приложения после вызова registerNetworkCallback().

  2. Затем приложение вызывает registerDefaultNetworkCallback(). Обратный вызов сети по умолчанию начинает получать вызовы onAvailable(), onNetworkCapabilitiesChanged() и onLinkPropertiesChanged() для мобильной сети, поскольку она является сетью по умолчанию. Если активна другая сеть, не заданная по умолчанию, приложение не сможет принимать вызовы для нее.

    Диаграмма состояний, на которой показано событие регистрации обратного вызова сети по умолчанию и обратные вызовы, активированные этим событием
    Рисунок 2. Состояние приложения после регистрации сети по умолчанию.

  3. Позже устройство подключается к сети Wi-Fi (без тарификации трафика). Обычный обратный вызов сети получает вызовы onAvailable(), onNetworkCapabilitiesChanged() и onLinkPropertiesChanged() для сети Wi-Fi.

    Диаграмма состояний, показывающая обратные вызовы, которые запускаются, когда приложение подключается к новой сети.
    Рисунок 3. Состояние приложения после подключения к сети Wi-Fi без тарификации трафика.

  4. На этом этапе проверка сети Wi-Fi может занять некоторое время. В этом случае вызовы onNetworkCapabilitiesChanged() для обратного вызова обычной сети не включают возможность NET_CAPABILITY_VALIDATED. Через некоторое время вызывается метод onNetworkCapabilitiesChanged(), в котором новые возможности включают NET_CAPABILITY_VALIDATED. В большинстве случаев проверка выполняется очень быстро.

    Когда сеть Wi-Fi проходит проверку, система предпочитает ее мобильной сети, главным образом потому, что она не тарифицируется. Сеть Wi-Fi становится сетью по умолчанию, поэтому обратный вызов сети по умолчанию получает вызов onAvailable(), onNetworkCapabilitiesChanged() и onLinkPropertiesChanged() для сети Wi-Fi. Мобильная сеть переходит в фоновый режим, а обратный вызов обычной сети получает вызов onLosing() для мобильной сети.

    Поскольку в этом примере предполагается, что мобильный интернет на устройстве всегда включен, мобильная сеть никогда не отключается. Если настройка отключена, то через некоторое время мобильная сеть отключается, а обычный обратный вызов сети получает вызов onLost().

    Диаграмма состояний, на которой показаны обратные вызовы, активируемые при проверке подключения к сети Wi-Fi.
    Рисунок 4. Состояние приложения после проверки сети Wi-Fi.

  5. Затем устройство внезапно отключается от Wi-Fi, потому что выходит из зоны действия сети. Поскольку Wi-Fi отключается, обычный обратный вызов сети получает вызов onLost() для Wi-Fi. Поскольку мобильная сеть теперь является сетью по умолчанию, обратный вызов сети по умолчанию получает вызовы onAvailable(), onNetworkCapabilitiesChanged() и onLinkPropertiesChanged() для мобильной сети.

    Диаграмма состояний, показывающая обратные вызовы, которые запускаются при потере подключения к сети Wi-Fi
    Рисунок 5. Состояние приложения после отключения от сети Wi-Fi.

Если параметр Мобильный интернет всегда активен отключен, то при отключении Wi-Fi устройство пытается подключиться к мобильной сети. Картина похожа, но с небольшой дополнительной задержкой для вызовов onAvailable(), а обычный обратный вызов сети также получает вызовы onAvailable(), onNetworkCapabilitiesChanged() и onLinkPropertiesChanged(), поскольку мобильная сеть становится доступной.

Ограничения на использование сети для передачи данных

Если сеть видна в обратном вызове, это не значит, что приложение может использовать ее для передачи данных. Некоторые сети не предоставляют подключение к интернету, а некоторые могут быть ограничены приложениями с привилегиями. Чтобы проверить подключение к интернету, ознакомьтесь со статьями NET_CAPABILITY_INTERNET и NET_CAPABILITY_VALIDATED.

Использование фоновых сетей также подлежит проверке разрешений. Если приложение использует фоновый мобильный трафик, ему требуется разрешение CHANGE_NETWORK_STATE.

Приложения с этим разрешением позволяют системе пытаться подключиться к сети, которая не используется, например к мобильной сети, когда устройство подключено к сети Wi-Fi. Такое приложение вызывает ConnectivityManager.requestNetwork(NetworkRequest, NetworkCallback) с NetworkCallback, который будет вызван при подключении к сети.