2025年3月31日 星期一 乙巳(蛇)年 正月初一 设为首页 加入收藏
rss
您当前的位置:首页 > 计算机 > 编程开发 > 安卓(android)开发

Android监听网络变化

时间:02-09来源:作者:点击数:43

小于Android5.0的版本

小于Android5.0的监听方式,使用广播接收者的方式,代码如下:

  • context.registerReceiver(ConnectivityReceiver(), IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION)) // 监听连接改变
  • class ConnectivityReceiver: BroadcastReceiver() {
  • /** 指示是否没有网络 */
  • var noConnectivity = false
  • var isFirst = true
  • @RequiresApi(Build.VERSION_CODES.N)
  • override fun onReceive(context: Context, intent: Intent) {
  • // 网络连接状态发生变化(Wifi、蜂窝),默认连接已建立或丢失。
  • // 受影响的网络的NetworkInfo作为附加发送。应该咨询一下发生了哪种连接事件。
  • // 由于NetworkInfo可能因UID而异,因此应用程序应始终通过getActiveNetworkInfo()获取网络信息。
  • val networkInfo: NetworkInfo? = intent.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO)
  • Timber.i("networkInfo = $networkInfo")
  • // ConnectivityManager.EXTRA_NETWORK_TYPE 此字段需求api为17,当前最小为15
  • val networkType = intent.getIntExtra("networkType", -1)
  • Timber.i("networkType = $networkType")
  • // 该关键字可提供(可选)有关网络状态的额外信息。信息可以从较低的网络层传递,并且含义可能特定于特定的网络类型。
  • val extraInfo = intent.getStringExtra(ConnectivityManager.EXTRA_EXTRA_INFO)
  • Timber.i("extraInfo = $extraInfo")
  • // 用于指示是否完全缺乏连通性,即没有网络可用
  • val noConnectivity = intent.getBooleanExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, false)
  • Timber.i("noConnectivity = $noConnectivity")
  • // ConnectivityManager.EXTRA_INET_CONDITION 隐藏字段,该键提供有关我们与整个Internet的连接的信息。 0表示没有连接,100表示连接良好
  • val inetCondition = intent.getIntExtra("inetCondition", -1)
  • Timber.i("inetCondition = $inetCondition")
  • Timber.fi("网络发生变化了,noConnectivity = $noConnectivity")
  • if (isFirst) {
  • // 第一次的网络状态,状态怎样都是要执行的
  • this.noConnectivity = noConnectivity
  • isFirst = false
  • } else if (this.noConnectivity == noConnectivity) {
  • // 非第一次的网络状态,需要判断是否与上一次一样,一样的话就不要重复执行了
  • // TODO 数据网络切WIFI时不会回调网络断开,而是直接回调网络有效,所以需要增加一个判断:如果之前是数据网络,现在变成了Wifi,则即使网络有连网状态和之前一样也要走后面的代码
  • return
  • }
  • this.noConnectivity = noConnectivity
  • if (noConnectivity) {
  • // TODO 没网了
  • } else {
  • // TODO 有网了
  • }
  • }
  • }

Android5.0或以上的版本

Android5.0或更高版本的监听方式,使用注册回调的方式,代码如下:

  • class ConnectivityEngine {
  • private val connectivityManager: ConnectivityManager by lazy { ContextHolder.getContext().getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager }
  • private var networkCallback: MyNetworkCallback? = null
  • @RequiresPermission(Manifest.permission.ACCESS_NETWORK_STATE)
  • fun registerNetworkChangeListener(callback: (Boolean, Network) -> Unit) {
  • val networkCallback = MyNetworkCallback(connectivityManager, callback).also { this.networkCallback = it }
  • if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
  • // 如果Android版本等于7.0(API 24)或以上
  • connectivityManager.registerDefaultNetworkCallback(networkCallback)
  • } else {
  • val networkRequest = NetworkRequest.Builder().build()
  • connectivityManager.registerNetworkCallback(networkRequest, networkCallback)
  • }
  • }
  • fun unregisterNetworkChangeListener() {
  • networkCallback?.let {
  • connectivityManager.unregisterNetworkCallback(it)
  • networkCallback = null
  • }
  • }
  • }
  • class MyNetworkCallback(private val connectivityManager: ConnectivityManager, private val callback: (Boolean, Network) -> Unit): ConnectivityManager.NetworkCallback() {
  • private var isCalledOnLost = true
  • private var isCalledOnAvailable = true
  • private var uiHandler = Handler(Looper.getMainLooper())
  • /** 在框架连接并声明可以使用新网络时调用。(在有网络的情况下注册监听器后这个函数就会立马被调用) */
  • override fun onAvailable(network: Network) {
  • super.onAvailable(network)
  • Timber.fi("onAvailable被调用,isCalledOnLost = $isCalledOnLost")
  • /*
  • 网络切换时,拿connectivityManager.activeNetwork来判断是不准确的,
  • 比如从一个wifi切换到另一个wifi时会先断开当前wifi,然后立马回调onAvailable,网络变成了移动数据网络。
  • 之后连上另一个wifi时又回调onAvailable,网络变成了wifi网络。
  • 在变成移动数据网络的时候,拿connectivityManager.activeNetwork来判断是否是移动网络显示为false,
  • 而使用onAvailable中的参数network来判断则为true。
  • */
  • connectivityManager.getNetworkCapabilities(network)?.let { capabilities ->
  • val isCurrentWifiNetwork = capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)
  • val isCurrentMobileNetwork = capabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)
  • val isCurrentVpn = capabilities.hasTransport(NetworkCapabilities.TRANSPORT_VPN)
  • val isInternet = capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
  • if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
  • val isValidated = capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)
  • // 插有sim卡且没连wifi时isCellular为true,此时连上wifi,isCellular为false。
  • Timber.fi("onAvailable:isInternet = $isInternet, isValidated = $isValidated, isWifi = $isCurrentWifiNetwork, isCellular = $isCurrentMobileNetwork, isVPN = $isCurrentVpn")
  • } else {
  • Timber.fi("onAvailable:isInternet = $isInternet, isWifi = $isCurrentWifiNetwork, isCellular = $isCurrentMobileNetwork, isVPN = $isCurrentVpn")
  • }
  • }
  • // 在启动VPN时(在Android10上会这样,在Android7.1.1不会有任何的方法回调),启动成功后会直接再回调onAvailable方法(不回调onLost),网络变成WIFI + VPN或进CELLULAR + VPN
  • // 注:不要使用VpnManager.isOnline()来判断,这个并不准确。从wifi vpn切换到APN网时,VPN图标还在,而且VpnManager.isOnline()返回true,实际上此时的VPN已经失效
  • // 切换网络时,如果VPN没有断开,则不需要做任何处理,因为ip会使用VPN的ip,数据请求权限什么都不会变的。
  • // 注:在android10系统中,切换网络后如果VPN没有断开,则不会执行onAvailable方法回调
  • // 在Android7.1.1 VPN连着时,切换网络VPN就会断开,在Android10不会断开,但是在Android10不回调onAvailable(注:Android10启动VPN成功后会再回调此方法,这种情况也是不需要执行后面代码的)
  • // 从移动数据网络切换到wifi网络,此时系统不会回调onLost方法,而是直接回调onAvailable。此时再断开Wifi会回调onLost方法,接着回调onAvailable变成移动数据网络
  • // 在Android10(在Android7.1.1中试验是会调用onAvailable的,因为切换时VPN会断开),开着VPN,移动数据网络切换到wifi,只回调onCapabilitiesChanged方法。如果没开VPN,则会回调onAvailable方法。WIFI开着VPN切换到移动网也只回调onCapabilitiesChanged
  • // 开着VPN切换网络只回调onCapabilitiesChanged的前提应该是切换后VPN并没有断开,如果断开了应该会回调onAvailable方法
  • // 从一个wifi切换到另一个wifi,先回调onLost,然后onAvailable是移动网络,wifi连上后又回调一次onAvailable是wifi网络
  • isCalledOnAvailable = true
  • if (!isCalledOnLost) {
  • return // 如果没有调用过onLost函数,则不往下执行了,预防onAvailable函数连续执行多次的情况
  • }
  • isCalledOnLost = false // 预防onAvailable函数连续执行多次的情况
  • Timber.fi("有网了")
  • uiHandler.post { callback(true, network) }
  • }
  • override fun onCapabilitiesChanged(network: Network, networkCapabilities: NetworkCapabilities) {
  • super.onCapabilitiesChanged(network, networkCapabilities)
  • Timber.fi("onCapabilitiesChanged,networkCapabilities = $networkCapabilities")
  • }
  • /** 当网络断开连接或不再满足此请求或回调时调用。(在无网络的情况下注册监听器后这个函数不会被调用) */
  • override fun onLost(network: Network) {
  • super.onLost(network)
  • Timber.fi("onLost被调用,isCalledOnAvailable = $isCalledOnAvailable")
  • isCalledOnLost = true
  • if (!isCalledOnAvailable) {
  • return // 如果没有调用过onAvailable函数,则不往下执行了,预防onLost函数连续执行多次的情况
  • }
  • isCalledOnAvailable = false // 预防onLost函数连续执行多次的情况
  • Timber.fi("没网了")
  • uiHandler.post { callback(false, network) }
  • }
  • }
方便获取更多学习、工作、生活信息请关注本站微信公众号城东书院 微信服务号城东书院 微信订阅号
推荐内容
相关内容
栏目更新
栏目热门