读取网络状态

Android 使应用程序能够了解连接性的动态变化。使用以下类跟踪和响应连接性变化

  • ConnectivityManager 会告诉您的应用程序有关系统连接状态的信息。
  • Network 类表示设备连接到的网络之一。您可以使用 Network 对象作为键来收集有关网络的信息 ConnectivityManager 或在网络上绑定套接字。当网络断开连接时,Network 对象将不再可用。即使设备稍后重新连接到同一设备,新的 Network 对象将表示新网络。
  • LinkProperties 对象包含有关网络链接的信息,例如为网络安装的 DNS 服务器、本地 IP 地址和网络路由列表。
  • NetworkCapabilities 对象包含有关网络属性的信息,例如传输(Wi-Fi、移动、蓝牙)以及网络的功能。例如,您可以查询该对象以确定网络是否能够发送 MMS、是否位于受限门户后面或是否计量。

对任何给定时间的连接状态感兴趣的应用程序可以调用 ConnectivityManager 方法以找出可用的网络类型。这些方法有助于调试,并且偶尔可以查看任何给定时间的连接快照。

但是,同步 ConnectivityManager 方法不会告诉您的应用程序关于调用后发生的任何事件,因此它们不会让您更新 UI。它们也不能根据网络断开连接或网络功能更改来调整应用程序行为。

连接性随时可能发生变化,大多数应用都需要始终保持设备网络状态的最新视图。应用可以向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

NetworkCapabilitiesLinkProperties对象提供有关系统了解的网络的所有属性的信息。

LinkProperties对象了解路由、链接地址、接口名称、代理信息(如果有)和 DNS 服务器。调用LinkProperties对象上的相关方法以检索您需要的信息。

NetworkCapabilities对象封装了有关网络传输及其功能的信息。

传输是对网络运行的物理介质的抽象。传输的常见示例包括以太网、Wi-Fi 和移动网络。VPN 和点对点 Wi-Fi 也可以是传输。在 Android 上,网络可以同时具有多个传输。一个例子是 VPN 同时在 Wi-Fi 和移动网络上运行。VPN 具有 Wi-Fi、移动和 VPN 传输。要确定网络是否具有特定传输,请使用NetworkCapabilities.hasTransport(int)方法,并使用NetworkCapabilities.TRANSPORT_*常量之一。

功能描述网络的属性。示例功能包括MMSNOT_METEREDINTERNET。具有 MMS 功能的网络可以发送和接收多媒体消息服务消息,而没有此功能的网络则不能。具有NOT_METERED功能的网络不会向用户收取数据费用。您的应用可以使用NetworkCapabilities.hasCapability(int)方法,并使用NetworkCapabilities.NET_CAPABILITY_*常量之一来检查适当的功能。

最有用的是NET_CAPABILITY_*常量包括

  • NET_CAPABILITY_INTERNET:表示网络已设置为访问互联网。这与设置有关,而不是实际能够访问公共服务器。例如,网络可以设置为访问互联网,但可能会受到网络接入点 (captive portal) 的限制。

    运营商的移动网络通常具有INTERNET功能,而本地点对点 Wi-Fi 网络通常没有。对于实际连接,请参阅NET_CAPABILITY_VALIDATED

  • NET_CAPABILITY_NOT_METERED:表示网络未计量。当用户对该连接上的大量数据使用量很敏感时,网络被分类为计量网络,因为这会产生货币成本、数据限制或电池性能问题。

  • NET_CAPABILITY_NOT_VPN:表示网络不是虚拟专用网络。

  • NET_CAPABILITY_VALIDATED:表示网络在探测时提供对公共互联网的实际访问。位于网络接入点 (captive portal) 背后的网络或不提供域名解析的网络不具有此功能。这是系统最接近地告诉有关网络实际提供访问的功能,尽管经过验证的网络原则上仍然可能受到基于 IP 的过滤的影响,或者由于信号差等问题而突然失去连接。

  • NET_CAPABILITY_CAPTIVE_PORTAL:表示网络在探测时具有网络接入点 (captive portal)。

还有其他一些更专业的应用程序可能感兴趣的功能。有关更多信息,请阅读NetworkCapabilities.hasCapability(int)中的参数定义。

网络的功能可以随时更改。当系统检测到网络接入点 (captive portal) 时,它会显示一个通知,邀请用户登录。在此过程中,网络具有NET_CAPABILITY_INTERNETNET_CAPABILITY_CAPTIVE_PORTAL功能,但没有NET_CAPABILITY_VALIDATED功能。

当用户采取行动并登录到网络接入点 (captive portal) 页面时,设备将能够访问公共互联网,并且网络将获得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 消息,请向 NetworkRequest 中添加 NET_CAPABILITY_MMS,以避免收到关于所有无法发送 MMS 消息的网络的通知。如果你的应用只对 P2P Wi-Fi 连接感兴趣,请添加 TRANSPORT_WIFI_AWARE。如果你对与互联网上服务器传输数据的能力感兴趣,NET_CAPABILITY_INTERNETNET_CAPABILITY_VALIDATED 会很有用。

示例回调序列

本节描述了一个应用在一个具有移动连接的设备上注册了默认回调和常规回调后可能收到的回调序列。在本例中,设备连接到一个良好的 Wi-Fi 接入点,然后断开与它的连接。本例还假设设备启用了“始终启用移动数据”设置。

时间线如下:

  1. 当应用调用 registerNetworkCallback() 时,回调会立即从移动网络的 onAvailable()onNetworkCapabilitiesChanged()onLinkPropertiesChanged() 接收调用,因为只有该网络可用。如果另一个网络可用,应用也会收到其他网络的回调。

    State diagram showing the register network callback event and the callbacks triggered by the event
    **图 1.** 调用 registerNetworkCallback() 后的应用状态。

  2. 然后,应用调用 registerDefaultNetworkCallback()。默认网络回调开始接收对移动网络的 onAvailable()onNetworkCapabilitiesChanged()onLinkPropertiesChanged() 的调用,因为移动网络是默认网络。如果另一个非默认网络处于启动状态,应用则无法接收非默认网络的调用。

    State diagram showing register the default network callback event and the
callbacks triggered by the event
    **图 2.** 注册默认网络后的应用状态。

  3. 稍后,设备连接到一个(无计量)Wi-Fi 网络。常规网络回调会收到针对 Wi-Fi 网络的 onAvailable()onNetworkCapabilitiesChanged()onLinkPropertiesChanged() 的调用。

    State diagram showing the callbacks triggered when the app connects to a
new network
    **图 3.** 连接到无计量 Wi-Fi 网络后的应用状态。

  4. 此时,Wi-Fi 网络可能需要一段时间才能验证。在这种情况下,常规网络回调的 onNetworkCapabilitiesChanged() 调用不包含功能 NET_CAPABILITY_VALIDATED。一小段时间后,它会收到一个对 onNetworkCapabilitiesChanged() 的调用,其中新的功能包括 NET_CAPABILITY_VALIDATED。在大多数情况下,验证非常快。

    当 Wi-Fi 网络验证后,系统会优先使用它而不是移动网络,主要是因为它是非计量的。Wi-Fi 网络成为默认网络,因此默认网络回调会收到针对 Wi-Fi 网络的 onAvailable()onNetworkCapabilitiesChanged()onLinkPropertiesChanged() 的调用。移动网络转到后台,常规网络回调会收到针对移动网络的 onLosing() 调用。

    因为本例假设此设备始终启用移动数据,所以移动网络永远不会断开连接。如果该设置被关闭,则一段时间后,移动网络会断开连接,常规网络回调会收到对 onLost() 的调用。

    State diagram showing the callbacks triggered when a Wi-Fi network
connection validates
    **图 4.** Wi-Fi 网络验证后的应用状态。

  5. 更晚的时候,设备突然断开与 Wi-Fi 的连接,因为它超出了范围。由于 Wi-Fi 断开连接,常规网络回调会收到针对 Wi-Fi 的 onLost() 调用。由于移动网络是新的默认网络,默认网络回调会收到针对移动网络的 onAvailable()onNetworkCapabilitiesChanged()onLinkPropertiesChanged() 的调用。

    State diagram showing the callbacks triggered when a Wi-Fi network
connection is lost
    **图 5.** 断开与 Wi-Fi 网络连接后的应用状态。

如果“始终启用移动数据”设置被关闭,则当 Wi-Fi 断开连接时,设备会尝试重新连接到移动网络。画面类似,但 onAvailable() 调用会稍微延迟,并且常规网络回调也会收到 onAvailable()onNetworkCapabilitiesChanged()onLinkPropertiesChanged() 的调用,因为移动网络变得可用。

对使用网络进行数据传输的限制

能够使用网络回调看到一个网络并不意味着你的应用可以将该网络用于数据传输。一些网络不提供互联网连接,而另一些网络可能仅限于特权应用使用。要检查互联网连接,请参阅 NET_CAPABILITY_INTERNETNET_CAPABILITY_VALIDATED.

后台网络的使用也受权限检查的约束。如果你的应用想要使用后台网络,则需要 CHANGE_NETWORK_STATE 权限。

具有此权限的应用会让系统尝试启动一个未启动的网络,例如当设备连接到 Wi-Fi 网络时,启动移动网络。这样的应用会调用 ConnectivityManager.requestNetwork(NetworkRequest, NetworkCallback),并使用一个 NetworkCallback 来在网络启动时调用。