获取当前的网络类型,即想要知道当前的网络是wifi还是蜂窝网络还是网线:
fun getNetworkPreferences(context: Context): String {val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManagerval activeNetwork = connectivityManager.activeNetwork ?: return "No active network"val capabilities = connectivityManager.getNetworkCapabilities(activeNetwork) ?: return "Unknown capabilities"return when {capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) -> "Wi-Fi"capabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) -> "Cellular"capabilities.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET) -> "Ethernet"else -> "Other"}
}
早期,修改网络可以使用如下方法:
ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
connectivityManager.setNetworkPreference(ConnectivityManager.TYPE_WIFI);
该方法会尝试根据指定的网络类型来设置设备的默认网络连接。它影响全局网络行为,而不仅仅是当前应用。这个方法自 API 21 (Android 5.0) 开始已经被废弃,因此在现代 Android 开发中,它不再有实际的效果。Google 推荐使用更细粒度的网络请求管理方式(如 NetworkRequest 和 NetworkCallback)来实现类似的功能。从 Android 6.0(API 23)开始,应用无法直接更改全局网络设置,只能引导用户手动修改或通过请求网络临时切换。权限要求:
- 获取网络状态需要
ACCESS_NETWORK_STATE
权限。 - 临时切换网络需要
CHANGE_NETWORK_STATE
权限。
如果要修改网络,例如从移动数据切换到Wi-Fi,这需要用户交互,并且无法直接通过普通应用完成,以下是几种间接方法:
-
引导用户到网络设置界面
fun openNetworkSettings(context: Context) {val intent = Intent(Settings.ACTION_WIRELESS_SETTINGS)context.startActivity(intent) }
这会打开网络设置界面,这个界面上有WLAN、移动网络、飞行模式、Ethernet等的选项,方便我们对各种网络进行设置。对于打开网络设置的代码还有一个:
fun openMobileNetworkSettings(context: Context) {val intent = Intent(Settings.ACTION_NETWORK_OPERATOR_SETTINGS)context.startActivity(intent) }
这个是跳到网络设置中的“移动网络设置”,即专门设置移动网络的,比如设置5G、4G、APN、移动数据开关等。
-
使用
NetworkRequest
、NetworkCallback
、ConnectivityManager
可以动态请求并使用符合特定条件的网络。fun requestCellularNetwork(context: Context) {val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManagerval request = NetworkRequest.Builder().addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR).build()connectivityManager.requestNetwork(request, object : ConnectivityManager.NetworkCallback() {override fun onAvailable(network: android.net.Network) {// 切换到此网络connectivityManager.bindProcessToNetwork(network)}}) }
注:这里做了两件事,首先是请求了蜂窝网络,然后请求到之后再将我们的应用绑定到这个网络,这样我们的应用就只会使用蜂窝网络,即使现在连接上wifi,其它的应用会使用wifi的流量,而我们的应用依然只使用蜂窝网络的流量,直到我们调用解除绑定:
ConnectivityManager.unbindProcessFromNetwork()
或ConnectivityManager.setProcessDefaultNetwork(null)
。
如果不想请求任何网络,有什么用什么,但是我想持续监听网络的变化,比如什么时候网络断开了,什么时候网络恢复了,可使用registerNetworkCallback
函数,示例如下:
val networkRequest = NetworkRequest.Builder().addTransportType(NetworkCapabilities.TRANSPORT_WIFI).build()val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
connectivityManager.registerNetworkCallback(networkRequest, object : ConnectivityManager.NetworkCallback() {override fun onAvailable(network: Network) {super.onAvailable(network)// 网络已可用}override fun onLost(network: Network) {super.onLost(network)// 网络丢失}
})
这里我们只监听wifi网络的变化,如果蜂窝网络发生变化我们就不知道,如果我只想知道当前默认网络的变化,默认网络即当前正在使用的网络,比如你当前有蜂窝网络,同时也连着WIFI,如果你的应用正在使用WIFI网络来进行网络传输,则WIFI网络为默认网络,不论当前默认是什么网络,想要监听它的变化可以使用registerDefaultNetworkCallback
,示例如下:
val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
connectivityManager.registerDefaultNetworkCallback(object : ConnectivityManager.NetworkCallback() {override fun onAvailable(network: Network) {super.onAvailable(network)// 当前默认网络可用}override fun onLost(network: Network) {super.onLost(network)// 当前默认网络丢失}
})
方法 | 适用场景 | 触发时机 | 主要区别 |
---|---|---|---|
requestNetwork() | 请求某个特定类型的网络连接(如 Wi-Fi、蜂窝)并等待连接。 | 网络可用时触发 onAvailable(),不可用时触发 onUnavailable() | 用于请求网络连接,操作较为临时。 |
registerNetworkCallback() | 持续监听特定网络类型(Wi-Fi、蜂窝等)的状态变化。 | 网络可用、丢失、切换时触发相应的回调。 | 用于长期监听指定网络类型的状态变化。 |
registerDefaultNetworkCallback() | 只关心设备的当前默认网络(如 Wi-Fi 或蜂窝网络)。 | 默认网络的变化(如切换 Wi-Fi、蜂窝数据等)触发。 | 用于监听设备默认网络的状态变化,适用于对网络类型不关心的场景。 |
在通过 ConnectivityManager.registerNetworkCallback()
注册网络回调时,onUnavailable()
方法不会被调用。onUnavailable()
主要是在 requestNetwork()
请求网络时,系统无法满足网络请求时触发的回调。
注册网络回调时的回调触发情况:
onAvailable()
:当您注册的网络回调监听的网络类型(如 Wi-Fi、蜂窝网络等)变为可用时触发。onLost()
:当设备断开与某个网络的连接时触发。比如设备当前使用的网络断开,或者网络切换发生时。
requestNetwork()、registerNetworkCallback()、registerDefaultNetworkCallback()会触发的所有回调汇总:
-
requestNetwork()
:onAvailable()
:请求的网络成功可用时触发。onUnavailable()
:请求的网络无法满足时触发。(根据我的实验,不论是请求wifi还是移动网络,当请求的网络无法满足时,这个函数都不会被调用。我在两部手机上试的,不知道别的手机会不会有不同?)onLost()
:请求的网络丢失时触发。
-
registerNetworkCallback()
:onAvailable()
:当指定的网络类型变为可用时触发。onLost()
:当指定的网络类型丢失时触发。onCapabilitiesChanged()
:当网络的能力发生变化时触发(如带宽、延迟等)。onLinkPropertiesChanged()
:当网络的链路属性变化时触发(如 IP 地址变化)。
-
registerDefaultNetworkCallback()
:onAvailable()
:当设备的默认网络变为可用时触发。onLost()
:当设备的默认网络丢失时触发。onCapabilitiesChanged()
:当默认网络的能力发生变化时触发(如网络速度、延迟变化等)。
为什么registerDefaultNetworkCallback()
不会触发onLinkPropertiesChanged()
?
-
onLinkPropertiesChanged()
: 这个回调通常在registerNetworkCallback()
方法中触发,用于监听 指定网络类型(如 Wi-Fi、蜂窝数据等)在连接时链路属性(LinkProperties
)发生变化。链路属性包括 IP 地址、DNS 设置、网络接口等信息。它是为了监听更详细的网络层信息变化,而不仅仅是网络的可用性。 -
registerDefaultNetworkCallback()
: 这个方法的设计目的是监听 默认网络 的可用性和丢失状态,但并不关心默认网络的链路属性变化(如 IP 地址变化)。因此,onLinkPropertiesChanged()
不会在registerDefaultNetworkCallback()
中触发。默认网络回调主要是关注网络的连接状态,而不是链路属性的细节变化。 -
onLinkPropertiesChanged()
只有在您使用registerNetworkCallback()
来监听特定网络类型时才会触发。也就是说,您需要关注某一特定的网络连接(例如 Wi-Fi 或蜂窝网络),并且当该网络的链路属性发生变化时,才会触发该回调。
设计目的:
registerDefaultNetworkCallback()
旨在提供设备 默认网络 的 连接/丢失状态变化,而onLinkPropertiesChanged()
更多是用于检测 网络接口 的 链路属性变化。这些链路属性变化不一定与默认网络的连接状态变化直接相关,因此onLinkPropertiesChanged()
不在默认网络回调中触发。
onCapabilitiesChanged()
和 onLinkPropertiesChanged()
都是 ConnectivityManager.NetworkCallback
的回调方法,它们在不同的网络状态变化时触发,主要用于监听网络的能力和链路属性的变化。尽管它们都是在网络状态发生变化时触发的,但触发的具体条件有所不同。
-
onCapabilitiesChanged()
触发时机:- onCapabilitiesChanged() 会在网络 能力(capabilities) 发生变化时被调用。网络能力通常是与网络的 功能性特征 相关的信息,比如:
- 网络是否支持互联网访问(NET_CAPABILITY_INTERNET)
- 是否支持漫游(NET_CAPABILITY_MMS)
- 网络的带宽、延迟、流量控制等能力
- 是否支持对等连接(P2P)
- 是否支持不加密的连接
- 网络的优先级等。
常见的触发场景:
- 网络从不支持互联网访问切换为支持互联网。
- 网络从高带宽模式切换到低带宽模式(例如,Wi-Fi 到 4G)。
- 网络的能力发生其他变化,例如,支持某种类型的流量(如视频流、音频流)或变更网络类型。
举例:当设备从 Wi-Fi 切换到蜂窝数据网络时,网络的能力会发生变化,因为蜂窝网络的能力(例如带宽、延迟、是否支持漫游等)和 Wi-Fi 网络有所不同。
- onCapabilitiesChanged() 会在网络 能力(capabilities) 发生变化时被调用。网络能力通常是与网络的 功能性特征 相关的信息,比如:
-
onLinkPropertiesChanged()
触发时机:- onLinkPropertiesChanged() 会在网络 链路属性(link properties) 发生变化时被调用。链路属性是与网络连接的 低层信息 相关的,比如:
- IP 地址
- DNS 配置
- 网络接口类型(例如 Wi-Fi 接口或蜂窝接口)
- 网络的路由设置
常见的触发场景:
- 网络的 IP 地址 发生变化(例如从 DHCP 获取新 IP)。
- 网络的 DNS 设置 发生变化。
- 网络的 路由配置 或 网络接口 发生变化。
例子:当设备的 Wi-Fi 网络断开并重新连接时,它的链路属性(如 IP 地址、DNS 设置等)可能会发生变化,因此会触发
onLinkPropertiesChanged()。
- onLinkPropertiesChanged() 会在网络 链路属性(link properties) 发生变化时被调用。链路属性是与网络连接的 低层信息 相关的,比如:
onCapabilitiesChanged()
和 onLinkPropertiesChanged()
的区别:
回调 | 触发时机 | 关注的变化 | 举例 |
---|---|---|---|
onCapabilitiesChanged() | 网络的能力(如支持互联网、带宽、延迟等)发生变化时触发 | 网络能力(例如支持互联网、P2P、带宽等) | 1. 网络从不支持互联网切换为支持互联网。2. 网络从 4G 切换为 3G,带宽发生变化。 |
onLinkPropertiesChanged() | 网络的链路属性(如 IP 地址、DNS、路由等)发生变化时触发 | 网络连接的低层属性(如 IP 地址、DNS) | 1. Wi-Fi 连接后,IP 地址或 DNS 设置变化。2. 使用 DHCP 获取新的 IP 地址。 |
对于connectivityManager.requestNetwork
函数请求一个网络,我当前手机有移动网络,我想请求一个Wi-Fi网络,我设备当前的Wi-Fi开关是关闭状态,当我调用requestNetwork()
函数后没有任何反应,而且onAvailable
、onUnavailable
和onLost
函数都没有被调用。然后我打开WIFI开关,回调函数还是没有被调用,然后我连接Wifi,此时onAvailable
被调用了,然后我断开wifi的连接,然后onLost
被调用了。然后我又连上wifi,onAvailable
又被调用了,再断开wifi(断开wifi或者关闭wifi开关),onLost
又被调用了。
即使我已经打开了wifi开关,并且连接了一个wifi,然后只是断开了wifi,但是这个wifi的账号密码还是保存状态的,此时调用 requestNetwork()
函数来请求wifi网络还是没有任何反应的,而且也没有回调被调用 。也是一样,当我手动连接上一个wifi后,onAvailable
被调用,断开wifi的连接后onLost
被调用,似乎onUnavailable
从来不会被调用,不知道是不是因为手机设备版本的问题,还是API设计就是如此。
当我wifi是连接的,移动网络也是ok的,此时App本来默认就是会走wifi网络的,此时我调用requestNetwork()
来请求移动网络,也是没有任何回调的,当我断开wifi时,移动网络变得可用了,此时之前的移动网络的请求中的onAvailable
回调会被调用。
所以我感觉这个requestNetwork()
真的就只是设置了一个监听器,跟registerNetworkCallback()
没多大差别,没有什么请求不请求的操作。
对于connectivityManager.bindProcessToNetwork(network)
绑定指定的网络,确实是可以的,比如我想绑定我的App就用移动网络,即使连了wifi也走移动的网络,不走wifi,经实验确实是OK的,wifi明明连接了,但是还是可以走移动的网络进行通信。反过来也一样,比如我想绑定WIFI网络,则当我断开WIFI后,即使现在移动网络是OK的,但是由于我绑定到了WIFI网络,则现在的网络请求就会失败,因为WIFI断开了,而且它不会走移动的网络。
requestNetwork()
请求网络,网络有效时再绑定网络,这样好像也行不通,比如现在同时也是连着wifi的,此时你调用requestNetwork()
请求移动网络时,onAvailable
是不会被调用的,那就没办法在这个回调中调用绑定的代码了,直到你断开wifi时onAvailable
才会被调用,如果wifi一直不断开就一直不会被调用。
所以,requestNetwork()
的应用场景是怎样的?我真是想不明白。
如果要绑定特定网络,可以这样做:
fun bindToMobileNetwork(context: Context) {val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManagerval networks: Array<Network> = connectivityManager.allNetworksfor (network in networks) {val capabilities = connectivityManager.getNetworkCapabilities(network)if (capabilities != null && capabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) {connectivityManager.bindProcessToNetwork(network)}}
}
如上代码是ChatGPT回答,但是经我实验也是行不通的,因为当wifi连接上之后,connectivityManager.allNetworks
返回的数组中只有一个Network
对象,且这个对象是Wifi
的,所以也是没办法绑定到移动网络的。
connectivityManager.allNetworks
明明返回的是一个数组,为什么我明明有移动网络,又连了WIFI,怎么返回的数组对象只有一个元素呢?这样的话,我猜测它应该是返回所有活跃的网络,活跃的网络即可以使用的网络,比如台式电脑可以接一台网线,然后再连上一个WIFI,此时电脑可以同时通往过网线或通过WIFI进行通信的,而且也不需要进行什么网络切换操作,这种情况我们就说网线网络和WIFI网络都是活跃的。再回到Android,由于系统做了限制,虽然我有移动网络和WIFI网络,但是系统只把WIFI设为活跃网络,此时我只能走WIFI的网络通信,移动网络是不通的,所以connectivityManager.allNetworks
中只包含了wifi网络。既然它是能返回一个数组,说明是可以返回多个网络,那就得看系统的实现了,如果系统允许多个活跃网络同时存在,那么这个数组里面就会包含有多个网络,可能在这种情况下,我们调用requestNetwork()
时onAvailable
就会被执行了。
根据ChartGPT回答,说requestNetwork()
本身不会强制切换网络,只会通知你请求的网络何时可用。且onUnavailable()
是在请求网络时,且网络不可用时调用,但是根据我的实验,不管我请求wifi或是移动网络,但是请求的网络不可用时,onUnavailable()
也不会被调用,只是当网络可用时onAvailable()
会被调用,然后网络断开时onLost
会被调用。
基于实验,我感觉requestNetwork()
就是个鸡肋,反正我试过两部手机是没用的,这个设计的初衷应该是想让网络切换的,但是手机厂商基于使用的便利性都进行了修改,因为Wifi连接上了,确实就应该自动使用wifi的网络,而wifi断开后就自动使用移动网络,当连接wifi时,如果wifi信号很差,也可以自动切换到使用移动网络,这些工作都是由系统或搬家厂商实现的,但是有时候我就想直接绑定应该使用移动流量,但是requestNetwork()
没办法请求到移动网络(如果当前wifi是连接着的话)。
但是绑定网络的那个函数是真的有用的:bindProcessToNetwork(network)
,一旦绑定了某个网络,你的app就只会使用这个网络进行通信了,除非你解除绑定,或者你的应用进程死了然后重新启动。