读取网络状态

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

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

随时关注即时连接状态的应用可以调用 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

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

LinkProperties 对象知道路由、链路地址、接口名称、代理信息(如果有)和 DNS 服务器。在 LinkProperties 对象上调用相关方法即可检索所需信息。

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

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

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

最有用的 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_INTERNETNET_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() 来衡量带宽,并使用包含 NET_CAPABILITY_NOT_METERED 参数的 NetworkCapabilites.hasCapability(int) 来确定是否计量。如需了解更多信息,请参阅有关 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_AWARENET_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 网络成为默认网络,因此默认网络回调会收到针对 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,以便在网络启动时调用。