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
NetworkCapabilities
和 LinkProperties
对象提供有关系统了解的网络的所有属性的信息。
LinkProperties
对象了解路由、链接地址、接口名称、代理信息(如果有)和 DNS 服务器。在 LinkProperties
对象上调用相关方法以检索您需要的信息。
NetworkCapabilities
对象封装了有关网络传输及其功能的信息。
传输是网络运行所依赖的物理介质的抽象。传输的常见示例包括以太网、Wi-Fi 和移动网络。VPN 和点对点 Wi-Fi 也可以是传输。在 Android 上,网络可以同时具有多个传输。一个示例是同时在 Wi-Fi 和移动网络上运行的 VPN。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
功能,而本地 P2P 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
对象包含大多数应用所需的常见功能,包括以下内容
在编写您的应用时,请检查默认值以查看它们是否与您的用例匹配,如果希望您的应用收到不具有这些功能的网络的通知,请清除它们。另一方面,添加功能以避免在您的应用不与之交互的任何网络的连接性更改时被调用。
例如,如果您的应用需要发送彩信,请将NET_CAPABILITY_MMS
添加到NetworkRequest
以避免被告知所有无法发送彩信的网络。如果您的应用仅对P2P Wi-Fi连接感兴趣,请添加TRANSPORT_WIFI_AWARE
。NET_CAPABILITY_INTERNET
和NET_CAPABILITY_VALIDATED
如果您对与互联网上的服务器传输数据的能力感兴趣,则很有帮助。
回调序列示例
本节描述了如果应用在具有移动连接的设备上同时注册默认回调和常规回调,则应用可能会获得的回调序列。在此示例中,设备连接到良好的Wi-Fi接入点,然后断开与它的连接。该示例还假设该设备启用了**始终保持移动数据连接**设置。
时间线如下
当应用调用
registerNetworkCallback()
时,回调会立即从onAvailable()
、onNetworkCapabilitiesChanged()
和onLinkPropertiesChanged()
接收来自移动网络的调用,因为只有该网络可用。如果另一个网络可用,则应用也会接收该网络的回调。
图 1. 调用registerNetworkCallback()
后的应用状态。然后,应用调用
registerDefaultNetworkCallback()
。默认网络回调开始接收对onAvailable()
、onNetworkCapabilitiesChanged()
和onLinkPropertiesChanged()
的调用,这些调用来自移动网络,因为移动网络是默认网络。如果另一个非默认网络处于活动状态,则应用无法接收该非默认网络的调用。
图 2. 注册默认网络后的应用状态。稍后,设备连接到(无计费)Wi-Fi网络。常规网络回调接收对
onAvailable()
、onNetworkCapabilitiesChanged()
和onLinkPropertiesChanged()
的调用,这些调用来自Wi-Fi网络。
图 3. 连接到无计费Wi-Fi网络后的应用状态。此时,Wi-Fi网络可能需要一段时间才能进行验证。在这种情况下,常规网络回调的
onNetworkCapabilitiesChanged()
调用不包含功能NET_CAPABILITY_VALIDATED
。短暂时间后,它会收到对onNetworkCapabilitiesChanged()
的调用,其中新的功能包括NET_CAPABILITY_VALIDATED
。在大多数情况下,验证非常快。当Wi-Fi网络验证后,系统会更倾向于选择它而不是移动网络,主要是因为它是无计费的。Wi-Fi网络成为默认网络,因此默认网络回调会收到对
onAvailable()
、onNetworkCapabilitiesChanged()
和onLinkPropertiesChanged()
的调用,这些调用来自Wi-Fi网络。移动网络转到后台,常规网络回调会收到对移动网络的onLosing()
的调用。因为此示例假设此设备始终保持移动数据连接,所以移动网络永远不会断开连接。如果该设置已关闭,则一段时间后移动网络会断开连接,常规网络回调会收到对
onLost()
的调用。
图 4. Wi-Fi网络验证后的应用状态。稍后,设备突然断开与Wi-Fi的连接,因为它超出了范围。由于Wi-Fi断开连接,常规网络回调会收到对Wi-Fi的
onLost()
的调用。由于移动网络是新的默认网络,因此默认网络回调会收到对onAvailable()
、onNetworkCapabilitiesChanged()
和onLinkPropertiesChanged()
的调用,这些调用来自移动网络。
图 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
在网络启动时进行调用。