Android 監聽網絡狀態變化(無切換中間態版)

需求:

  • 獲取當前的網絡狀態與類型(WIFI、數據流量)
  • 監聽網絡狀態真正變化
  • 監聽網絡類型發生變化

業務場景:

  • 用戶打開 App 時、使用過程中,出現無網絡時,顯示 Toast 提示。但當 wifi、數據流量 互相切換的過程中不要有提示。
  • 下載功能支持檢測到用戶連接上 wifi 時開啟靜默下載,當換成 數據流量 時停止靜默下載。

需求分析:

獲取當前網絡狀態與類型

即提供兩個方法,用于獲取當前的網絡狀態、網絡類型。

監聽網絡狀態真正變化

網絡狀態真正變化,首先明確網絡狀態只有【有網】與【無網】,所以當 WIFI 和 數據流量 同時開啟的情況下,僅關閉一項,不會提示網絡狀態的變更。

監聽網絡類型發生變化

網絡類型發生變化,是指當前使用的網絡類型發生變化。舉個例子,WIFI、數據流量同時開啟,理論上當前網絡類型是 WIFI,所以當僅關閉 數據流量 時,不會提示網絡類型變更,但僅關閉 WIFI 時,會提示網絡類型變更為 數據流量。
這里還需要注意一點,有一個“系統默認網絡”的概念:系統通常首選不按流量計費的網絡而非按流量計費的網絡,首選網速較快的網絡而非網速較慢的網絡。

技術實現:

常見監聽網絡狀態有三種方式:

  • 監聽廣播
  • ConnectivityManager#registerDefaultNetworkCallback()
  • ConnectivityManager#registerNetworkCallback()

下面逐一實現看效果:

監聽網絡狀態變化廣播

 

kotlin

復制代碼

class NetConnectReceiver: BroadcastReceiver() { override fun onReceive(context: Context?, intent: Intent?) { Log.e("qingshan", "網絡狀態改變") context?.getSystemService(ConnectivityManager::class.java)?.allNetworkInfo?.filter { it.typeName == "MOBILE" || it.typeName == "WIFI" }?.forEach {networkInfo -> Log.e("qingshan", "${networkInfo?.typeName}, isConnect= ${networkInfo?.isConnected}") } } } //動態注冊廣播監聽 class CustomApplication: Application() { override fun onCreate() { super.onCreate() //這里模擬工具類場景:全局一個監聽,然后在工具類中分發。 registerReceiver(NetConnectReceiver(), IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION)) } }

效果:

  • 數據流量、wifi 全關,冷啟動。收到兩次廣播,均表示【數據流量、wifi 】無連接。
 

kotlin

復制代碼

2023-11-10 14:21:58.081 13917-13917 qingshan E 網絡狀態改變 2023-11-10 14:21:58.082 13917-13917 qingshan E MOBILE, isConnect= false 2023-11-10 14:21:58.082 13917-13917 qingshan E WIFI, isConnect= false 2023-11-10 14:21:58.083 13917-13917 qingshan E 網絡狀態改變 2023-11-10 14:21:58.083 13917-13917 qingshan E MOBILE, isConnect= false 2023-11-10 14:21:58.083 13917-13917 qingshan E WIFI, isConnect= false

  • 數據流量、wifi 全開,冷啟動。收到連續兩次廣播,均表示當前【WIFI】連接。
 

kotlin

復制代碼

2023-11-10 14:13:46.002 13917-13917 qingshan E 網絡狀態改變 2023-11-10 14:13:46.002 13917-13917 qingshan E MOBILE, isConnect= false 2023-11-10 14:13:46.002 13917-13917 qingshan E WIFI, isConnect= true 2023-11-10 14:13:46.003 13917-13917 qingshan E 網絡狀態改變 2023-11-10 14:13:46.003 13917-13917 qingshan E MOBILE, isConnect= false 2023-11-10 14:13:46.003 13917-13917 qingshan E WIFI, isConnect= true

  • 數據流量、wifi 全開,僅關閉流量。無廣播。
  • 數據流量、wifi 全開,僅關閉wifi(用于模擬斷開wifi 或 wifi網絡不佳時系統自動啟用數據流量)。收到多次廣播,有時表示先【數據流量、wifi 】無連接,然后又出現【數據流量】連接,有時均表示當前【數據流量】連接。
 

kotlin

復制代碼

2023-11-10 14:18:46.622 13917-13917 qingshan E 網絡狀態改變 2023-11-10 14:18:46.624 13917-13917 qingshan E MOBILE, isConnect= true 2023-11-10 14:18:46.624 13917-13917 qingshan E WIFI, isConnect= false 2023-11-10 14:18:46.624 13917-13917 qingshan E 網絡狀態改變 2023-11-10 14:18:46.624 13917-13917 qingshan E MOBILE, isConnect= true 2023-11-10 14:18:46.624 13917-13917 qingshan E WIFI, isConnect= false 2023-11-10 14:18:46.665 13917-13917 qingshan E 網絡狀態改變 2023-11-10 14:18:46.666 13917-13917 qingshan E MOBILE, isConnect= true 2023-11-10 14:18:46.666 13917-13917 qingshan E WIFI, isConnect= false 2023-11-10 14:18:46.666 13917-13917 qingshan E 網絡狀態改變 2023-11-10 14:18:46.666 13917-13917 qingshan E MOBILE, isConnect= true 2023-11-10 14:18:46.666 13917-13917 qingshan E WIFI, isConnect= false

有時日志如下:

 

kotlin

復制代碼

2023-11-10 14:25:03.460 13917-13917 qingshan E 網絡狀態改變 2023-11-10 14:25:03.461 13917-13917 qingshan E MOBILE, isConnect= false 2023-11-10 14:25:03.461 13917-13917 qingshan E WIFI, isConnect= false 2023-11-10 14:25:03.461 13917-13917 qingshan E 網絡狀態改變 2023-11-10 14:25:03.462 13917-13917 qingshan E MOBILE, isConnect= false 2023-11-10 14:25:03.462 13917-13917 qingshan E WIFI, isConnect= false 2023-11-10 14:25:03.527 13917-13917 qingshan E 網絡狀態改變 2023-11-10 14:25:03.528 13917-13917 qingshan E MOBILE, isConnect= true 2023-11-10 14:25:03.528 13917-13917 qingshan E WIFI, isConnect= false 2023-11-10 14:25:03.528 13917-13917 qingshan E 網絡狀態改變 2023-11-10 14:25:03.528 13917-13917 qingshan E MOBILE, isConnect= true 2023-11-10 14:25:03.528 13917-13917 qingshan E WIFI, isConnect= false

  • 數據流量、wifi 全關。收到兩次廣播,均表示【數據流量、wifi 】無連接。

注意:先關 wifi 再關數據時,先打印上面(“數據流量、wifi 全開,僅關閉wifi”場景)日志,再打印下面日志。

 

kotlin

復制代碼

2023-11-10 14:21:58.081 13917-13917 qingshan E 網絡狀態改變 2023-11-10 14:21:58.082 13917-13917 qingshan E MOBILE, isConnect= false 2023-11-10 14:21:58.082 13917-13917 qingshan E WIFI, isConnect= false 2023-11-10 14:21:58.083 13917-13917 qingshan E 網絡狀態改變 2023-11-10 14:21:58.083 13917-13917 qingshan E MOBILE, isConnect= false 2023-11-10 14:21:58.083 13917-13917 qingshan E WIFI, isConnect= false

  • 僅開數據流量,再開 wifi(用于模擬使用過程中自動連接上wifi)。收到多次廣播,均表示【wifi】連接。
 

kotlin

復制代碼

2023-11-10 14:31:21.285 13917-13917 qingshan E 網絡狀態改變 2023-11-10 14:31:21.286 13917-13917 qingshan E MOBILE, isConnect= false 2023-11-10 14:31:21.286 13917-13917 qingshan E WIFI, isConnect= true 2023-11-10 14:31:21.287 13917-13917 qingshan E 網絡狀態改變 2023-11-10 14:31:21.287 13917-13917 qingshan E MOBILE, isConnect= false 2023-11-10 14:31:21.287 13917-13917 qingshan E WIFI, isConnect= true 2023-11-10 14:31:21.317 13917-13917 qingshan E 網絡狀態改變 2023-11-10 14:31:21.319 13917-13917 qingshan E MOBILE, isConnect= false 2023-11-10 14:31:21.319 13917-13917 qingshan E WIFI, isConnect= true 2023-11-10 14:31:21.319 13917-13917 qingshan E 網絡狀態改變 2023-11-10 14:31:21.320 13917-13917 qingshan E MOBILE, isConnect= false 2023-11-10 14:31:21.320 13917-13917 qingshan E WIFI, isConnect= true

ConnectivityManager#registerDefaultNetworkCallback()

要求 android sdk >=24 用于監聽“系統默認網絡”發生變更。 請勿在回調中調用 ConnectivityManager 的同步方法來查找新可用網絡的屬性,因為這會受到競態條件的影響。例如:在 onLost() 回調中調用 connectivityManager.getNetworkCapabilities()。

 

kotlin

復制代碼

class CustomApplication: Application() { override fun onCreate() { super.onCreate() //這里模擬工具類場景:全局一個監聽,然后在工具類中分發。 getSystemService(ConnectivityManager::class.java).apply { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { registerDefaultNetworkCallback(object : NetworkCallback() { override fun onAvailable(network: Network) { super.onAvailable(network) Log.e("qingshan", "def -> onAvailable") } override fun onLost(network: Network) { super.onLost(network) Log.e("qingshan", "def -> onLost") } override fun onCapabilitiesChanged( network: Network, networkCapabilities: NetworkCapabilities ) { super.onCapabilitiesChanged(network, networkCapabilities) Log.e("qingshan", "def -> 可正常訪問網絡 = ${networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)} " + "& 數據連接 = ${networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)} " + "& wifi連接= ${networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)}") } }) } } } }

效果

  • 數據流量、wifi 全關,冷啟動。無回調觸發。
  • 數據流量、wifi 全開,冷啟動。觸發 onAvailable() 和 onCapabilitiesChanged() 回調。表示當前【WIFI】連接。
 

kotlin

復制代碼

2023-11-10 15:22:31.427 8342-12273 qingshan E def -> onAvailable 2023-11-10 15:22:31.427 8342-12273 qingshan E def -> 可正常訪問網絡 = true & 數據連接 = false & wifi連接= true

  • 數據流量、wifi 全開,僅關閉流量。無回調觸發。
  • 數據流量、wifi 全開,僅關閉wifi(用于模擬斷開wifi 或 wifi網絡不佳時系統自動啟用數據流量)。觸發一次 onLost() 和 onAvailable() 回調,觸發多次 onCapabilitiesChanged() 回調。表示當前【數據流量】連接。
 

kotlin

復制代碼

2023-11-10 15:30:26.103 8342-12273 qingshan E def -> onLost 2023-11-10 15:30:26.150 8342-12273 qingshan E def -> onAvailable 2023-11-10 15:30:26.151 8342-12273 qingshan E def -> 可正常訪問網絡 = true & 數據連接 = true & wifi連接= false

  • 數據流量、wifi 全關。數據流量和 wifi 誰先關閉觸發的回調不同,wifi 先關會觸發兩次 onLost() 回調。有時表示【數據流量、wifi】無連接,有時先表示【wifi】無連接,切換為【數據流量】連接,再表示【數據流量、wifi】無連接。

注意:先關 wifi 再關數據時,先打印上面(“數據流量、wifi 全開,僅關閉wifi”場景)日志,再打印下面日志。

 

kotlin

復制代碼

2023-11-10 15:35:42.923 8342-12273 qingshan E def -> onLost

  • 僅開數據流量,再開 wifi(用于模擬使用過程中自動連接上wifi)。觸發一次 onAvailable() 回調,觸發多次 onCapabilitiesChanged() 回調。表示當前【wifi】連接,但有時會有不同的網絡狀態。
 

kotlin

復制代碼

2023-11-10 15:33:05.596 8342-12273 qingshan E def -> onAvailable 2023-11-10 15:33:05.596 8342-12273 qingshan E def -> 可正常訪問網絡 = true & 數據連接 = false & wifi連接= true

有時日志如下:

 

kotlin

復制代碼

2023-11-10 15:39:22.644 8342-12273 qingshan E def -> onAvailable 2023-11-10 15:39:22.645 8342-12273 qingshan E def -> 可正常訪問網絡 = false & 數據連接 = true & wifi連接= false 2023-11-10 15:39:22.842 8342-12273 qingshan E def -> 可正常訪問網絡 = true & 數據連接 = true & wifi連接= false

ConnectivityManager#registerNetworkCallback()

用于監聽所有網絡發生變更。 請勿在回調中調用 ConnectivityManager 的同步方法來查找新可用網絡的屬性,因為這會受到競態條件的影響。例如:在 onLost() 回調中調用 connectivityManager.getNetworkCapabilities()。

 

kotlin

復制代碼

class CustomApplication: Application() { override fun onCreate() { super.onCreate() //這里模擬工具類場景:全局一個監聽,然后在工具類中分發。 getSystemService(ConnectivityManager::class.java).apply { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { registerNetworkCallback(NetworkRequest.Builder() .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR) .addTransportType(NetworkCapabilities.TRANSPORT_WIFI) .addCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED) .build(), object : NetworkCallback(){ override fun onAvailable(network: Network) { super.onAvailable(network) Log.e("qingshan", "all -> onAvailable") } override fun onLost(network: Network) { super.onLost(network) Log.e("qingshan", "all -> onLost") } override fun onCapabilitiesChanged( network: Network, networkCapabilities: NetworkCapabilities ) { super.onCapabilitiesChanged(network, networkCapabilities) Log.e("qingshan", "all -> 可正常訪問網絡 = ${networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)} " + "& 數據連接 = ${networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)} " + "& wifi連接= ${networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)}") } }) } } } }

效果

  • 數據流量、wifi 全關,冷啟動。無回調觸發。
  • 數據流量、wifi 全開,冷啟動。觸發兩次 onAvailable() 和 onCapabilitiesChanged() 回調,表示當前【數據流量、wifi】均連接。
 

kotlin

復制代碼

2023-11-10 17:24:37.153 10713-6397 qingshan E all -> onAvailable 2023-11-10 17:24:37.153 10713-6397 qingshan E all -> 可正常訪問網絡 = true & 數據連接 = false & wifi連接= true 2023-11-10 17:24:37.155 10713-6397 qingshan E all -> onAvailable 2023-11-10 17:24:37.155 10713-6397 qingshan E all -> 可正常訪問網絡 = true & 數據連接 = true & wifi連接= false

  • 數據流量、wifi 全開,僅關閉流量。觸發一次 onLost() 回調,表示當前有網絡關閉,具體是什么不知道。
 

kotlin

復制代碼

2023-11-10 17:26:28.243 10713-6397 qingshan E all -> onLost

  • 數據流量、wifi 全開,僅關閉wifi(用于模擬斷開wifi 或 wifi網絡不佳時系統自動啟用數據流量)。觸發一次 onLoast() 回調,表示當前有網絡關閉。因為關閉的是“系統默認網絡”,所以會觸發多次 onCapabilitiesChanged() 回調,表示當前變成【數據流量】連接。
 

kotlin

復制代碼

2023-11-10 17:30:43.302 10713-6397 qingshan E all -> onLost 2023-11-10 17:30:45.464 10713-6397 qingshan E all -> 可正常訪問網絡 = true & 數據連接 = true & wifi連接= false

  • 數據流量、wifi 全關。觸發兩次 onLost() 回調,但先 wifi 時會在兩次 onLost() 中間觸發一次 onCapabilitiesChanged() 回調。表示當前網絡逐個不可用。

先關wifi,再關數據流量,日志如下:

 

kotlin

復制代碼

2023-11-10 17:36:19.335 10713-6397 qingshan E all -> onLost 2023-11-10 17:36:19.475 10713-6397 qingshan E all -> 可正常訪問網絡 = true & 數據連接 = true & wifi連接= false 2023-11-10 17:36:20.802 10713-6397 qingshan E all -> onLost

先關數據流量,再關wifi,日志如下:

 

kotlin

復制代碼

2023-11-10 17:37:20.487 10713-6397 qingshan E all -> onLost 2023-11-10 17:37:22.176 10713-6397 qingshan E all -> onLost

  • 僅開數據流量,再開 wifi(用于模擬使用過程中自動連接上wifi)。會觸發一次 onAvailable(),觸發多次 onCapabilitiesChanged() 回調。表示當前【wifi】已連接(但不意味著正在使用的是 wifi,具體正在使用的網絡類型由系統決定,即系統默認網絡)。
 

kotlin

復制代碼

2023-11-10 17:33:42.284 10713-6397 qingshan E all -> onAvailable 2023-11-10 17:33:42.284 10713-6397 qingshan E all -> 可正常訪問網絡 = true & 數據連接 = false & wifi連接= true

  • 僅開wifi,再開數據流量。會觸發一次 onAvailable(),觸發多次 onCapabilitiesChanged() 回調。表示當前【數據流量】已連接(但不意味著正在使用的是數據流量,具體正在使用的網絡類型由系統決定,即系統默認網絡)。
 

kotlin

復制代碼

2023-11-10 17:27:49.875 10713-6397 qingshan E all -> onAvailable 2023-11-10 17:27:49.876 10713-6397 qingshan E all -> 可正常訪問網絡 = true & 數據連接 = true & wifi連接= false 2023-11-10 17:27:49.878 10713-6397 qingshan E all -> 可正常訪問網絡 = true & 數據連接 = true & wifi連接= false

總結

根據上面的實際運行效果可以得知:

  • ConnectivityManager#registerNetworkCallback() 是監聽所有網絡變換的,監聽范圍廣,但無法得知當前“系統默認網絡”是什么,可以實現判斷網絡狀態,但無法判斷網絡類型。
  • 廣播監聽 與 ConnectivityManager#registerDefaultNetworkCallback() 都是監聽“系統默認網絡”,所以可以實現網絡狀態與類型的判斷,但都存在重復回調的情況,所以要做過濾處理,以及“系統默認網絡”切換到普通網絡時會有偶現短暫“無網絡”狀態,需要做延遲處理。
  • 廣播監聽所使用的方式有標記為“廢棄”,同時 ConnectivityManager#registerDefaultNetworkCallback() 也有版本的限制,所以可以兩者結合使用,優先使用 egisterDefaultNetworkCallback(),廣播監聽用于兜底。

所以,綜合上述,得出如下工具類實現:

 

kotlin

復制代碼

import android.content.BroadcastReceiver import android.content.Context import android.content.Intent import android.content.IntentFilter import android.net.ConnectivityManager import android.net.Network import android.net.NetworkCapabilities import android.os.Build import android.os.Handler import android.os.Looper sealed class ConnectType(val value: Int) { object Mobile : ConnectType(0) object Wifi : ConnectType(1) object None : ConnectType(-1) companion object { fun convert2Type(value: Int): ConnectType { return when (value) { Mobile.value -> Mobile Wifi.value -> Wifi else -> None } } } } object NetConnectManager { private var mConnectivityManager: ConnectivityManager? = null private val mainHandler = Handler(Looper.getMainLooper()) private val mNetTypeListener = mutableListOf<(type: ConnectType) -> Unit>() private val mNetStateListener = mutableListOf<(isAvailable: Boolean) -> Unit>() private var mCurrentConnectType: ConnectType? = null private var mIsNetAvailable: Boolean? = null /** * 初始化 */ fun init(context: Context) { mConnectivityManager = context.getSystemService(ConnectivityManager::class.java) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { mConnectivityManager?.registerDefaultNetworkCallback(DefaultNetConnectCallback()) } else { context.registerReceiver( NetConnectReceiver(), IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION) ) } } /** * 注冊網絡類型監聽 */ fun addNetTypeChangeListener(listener: (type: ConnectType) -> Unit) { mNetTypeListener.add(listener) } /** * 反注冊網絡類型監聽 */ fun removeNetTypeChangeListener(listener: (type: ConnectType) -> Unit) { mNetTypeListener.remove(listener) } /** * 注冊網絡狀態監聽 */ fun addNetStatusChangeListener(listener: (isAvailable: Boolean) -> Unit) { mNetStateListener.add(listener) } /** * 反注冊網絡狀態監聽 */ fun removeNetStatusChangeListener(listener: (isAvailable: Boolean) -> Unit) { mNetStateListener.remove(listener) } /** * 獲取當前網絡類型 */ fun getConnectType(): ConnectType { if (mConnectivityManager == null) { throw UninitializedPropertyAccessException("請先調用init()初始化") } return mCurrentConnectType ?: mConnectivityManager?.getNetworkCapabilities( mConnectivityManager?.activeNetwork ).let { return if (it?.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) == true) { ConnectType.Mobile } else if (it?.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) == true) { ConnectType.Wifi } else { ConnectType.None } } } /** * 獲取當前是否網絡已連接 */ fun isConnected(): Boolean { if (mConnectivityManager == null) { throw UninitializedPropertyAccessException("請先調用init()初始化") } return (mIsNetAvailable ?: mConnectivityManager?.getNetworkCapabilities(mConnectivityManager?.activeNetwork) ?.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)) == true } private class DefaultNetConnectCallback : ConnectivityManager.NetworkCallback() { override fun onLost(network: Network) { super.onLost(network) mCurrentConnectType = ConnectType.None mainHandler.postDelayed({ if (mCurrentConnectType == ConnectType.None && mIsNetAvailable == true) { mIsNetAvailable = false mNetStateListener.forEach { it.invoke(false) } mNetTypeListener.forEach { it(ConnectType.None) } } }, 500) } override fun onCapabilitiesChanged( network: Network, networkCapabilities: NetworkCapabilities ) { super.onCapabilitiesChanged(network, networkCapabilities) mainHandler.post { val isConnected = networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED) val isCellular = networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) val isWifi = networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) if (isConnected) { val newConnectType = if (isCellular) ConnectType.Mobile else if (isWifi) ConnectType.Wifi else ConnectType.None if (mIsNetAvailable == null || mIsNetAvailable == false) { mIsNetAvailable = true mNetStateListener.forEach { it(true) } } if (mCurrentConnectType != newConnectType) { mCurrentConnectType = newConnectType mNetTypeListener.forEach { it(newConnectType) } } } } } } private class NetConnectReceiver : BroadcastReceiver() { override fun onReceive(context: Context?, intent: Intent?) { val activityNetworkInfo = context?.getSystemService(ConnectivityManager::class.java)?.allNetworkInfo?.filter { (it.type == ConnectType.Mobile.value || it.type == ConnectType.Wifi.value) && it.isConnected }?.firstOrNull() if (activityNetworkInfo != null) { if (mIsNetAvailable == null || mIsNetAvailable == false) { mIsNetAvailable = true mNetStateListener.forEach { it(true) } } ConnectType.convert2Type(activityNetworkInfo.type).let { connectType -> if (connectType != mCurrentConnectType) { mCurrentConnectType = connectType mNetTypeListener.forEach { it(connectType) } } } return } mCurrentConnectType = ConnectType.None mainHandler.postDelayed({ if (mCurrentConnectType == ConnectType.None && mIsNetAvailable == true) { mIsNetAvailable = false mNetStateListener.forEach { it(false) } mNetTypeListener.forEach { it(ConnectType.None) } } }, 500) } } }

效果:

測試代碼:

 

kotlin

復制代碼

class MainActivity : AppCompatActivity(){ override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) Log.e("qingshan", "網絡是否連接= ${NetConnectManager.isConnected()} & 網絡類型= ${NetConnectManager.getConnectType()}") NetConnectManager.addNetTypeChangeListener { Log.e("qingshan", "網絡類型[監聽]= $it") } NetConnectManager.addNetStatusChangeListener { Log.e("qingshan", "網絡是否已連接[監聽]= $it") } } }

  • 數據流量、wifi 全關,冷啟動。
 

kotlin

復制代碼

qingshan E 網絡是否連接= false & 網絡類型= com.stefan.simpleskin.ConnectType$None@6f21c25

  • 數據流量、wifi 全開,冷啟動。
 

kotlin

復制代碼

qingshan E 網絡是否連接= true & 網絡類型= com.stefan.simpleskin.ConnectType$Wifi@34771fa qingshan E 網絡類型[監聽]= com.stefan.simpleskin.ConnectType$Wifi@34771fa

  • 數據流量、wifi 全開,僅關閉流量。 無監聽回調。
  • 數據流量、wifi 全開,僅關閉wifi(用于模擬斷開wifi 或 wifi網絡不佳時系統自動啟用數據流量)。
 

kotlin

復制代碼

qingshan E 網絡類型[監聽]= com.stefan.simpleskin.ConnectType$Mobile@cf38ed1

  • 數據流量、wifi 全關。
 

kotlin

復制代碼

qingshan E 網絡是否已連接[監聽]= false qingshan E 網絡類型[監聽]= com.stefan.simpleskin.ConnectType$None@fddad36

  • 僅開數據流量,再開 wifi(用于模擬使用過程中自動連接上wifi)。
 

kotlin

復制代碼

qingshan E 網絡類型[監聽]= com.stefan.simpleskin.ConnectType$Wifi@34771fa

  • 僅開wifi,再開數據流量。無監聽回調。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/39936.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/39936.shtml
英文地址,請注明出處:http://en.pswp.cn/web/39936.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

ppt接單渠道大公開??

PPT 接單主要分兩種&#xff1a;PPT 模板投稿和PPT 定制接單&#xff0c;我們先從簡單的 PPT 模板投稿說起。 PPT 模板投稿 利用業余時間&#xff0c;做一些 PPT 模板上傳到平臺&#xff0c;只要有人下載你的模板&#xff0c;你就有收入。如果模板質量高&#xff0c;簡直就是一…

【設計模式】觀察者模式(定義 | 特點 | Demo入門講解)

文章目錄 定義結構Demo | 代碼Subject目標類Observer抽象觀察者觀察者1 | CPU監聽器觀察者2 | 內存監聽器客戶端 | Client 優點適合場景 定義 所謂觀察者模式就是你是被觀察的那個對象&#xff0c;你爸爸媽媽就是觀察者&#xff0c;一天24h盯著你&#xff0c;一旦你不聽話&…

【BUUCTF-PWN】7-[第五空間2019 決賽]PWN5

參考&#xff1a;BUU pwn [第五空間2019 決賽]PWN5 //格式化字符串漏洞 - Nemuzuki - 博客園 (cnblogs.com) 格式化字符串漏洞原理詳解_printf 任意內存讀取-CSDN博客 32位小端排序&#xff0c;有棧溢出保護 運行效果&#xff1a; 查看main函數 存在格式化字符串漏洞 輸…

SQL二次注入原理分析

二次注入在測試的時候比較少見&#xff0c;或者說很難被測出來&#xff0c;因為測的時候首先要去找注入的位置&#xff0c;其次是去判斷第一次執行的SQL語句&#xff0c;然后還要去判斷第二次進行調用的 SQL 語句。而關鍵問題就出在第二次的調用上面。 下面以一個常用過濾方法…

macos下搭建minikube dashboard的啟動

背景 最近在復習一下k8s環境相關的知識&#xff0c;需要在自己電腦上搭建一個minikube的環境供自己使用。但是因為docker的鏡像倉庫最近被墻了&#xff0c;因此在執行minikube dashboard的時候&#xff0c;拉不到相應的鏡像&#xff0c;就導致頁面看不到相應的一些信息因此本文…

【PYG】dataloader和densedataloader

DenseDataLoader 是專門用于處理稠密圖數據的&#xff0c;而 DataLoader 通常用于處理稀疏圖數據。兩者的主要區別在于它們的輸入數據格式和處理方式。DenseDataLoader 適合處理固定大小的鄰接矩陣和節點特征矩陣的數據&#xff0c;而 DataLoader 更加靈活&#xff0c;可以處理…

flask中解決圖片不顯示的問題(很細微的點)

我在編寫flask項目的時候&#xff0c;在編寫html的時候&#xff0c;發現不管我的圖片路徑如何變化&#xff0c;其就是顯示不出來。如下圖我框中的地方。 我嘗試過使用瀏覽器打開&#xff0c;是可以的。 一旦運行這個flask項目&#xff0c;就無法顯示了。 我查閱資料后。發現…

簡易版async/await

參考&#xff1a;https://juejin.cn/post/7007031572238958629?searchId20240704101813568E9B5B1013C881A239#heading-15 總結一下async/await的知識點 1、 await只能在async函數中使用&#xff0c;不然會報錯 2、 async函數返回的是一個Promise對象&#xff0c;有無值看有…

泛微開發修煉之旅--29用計劃任務定時發送郵件提醒

文章鏈接&#xff1a;29用計劃任務定時發送郵件提醒

[單master節點k8s部署]17.監控系統構建(二)Prometheus安裝

prometheus server安裝 創建sa賬號&#xff0c;對prometheus server進行授權。因為Prometheus是安裝在pod里面&#xff0c;以pod的形式去運行的&#xff0c;因此需要創建sa&#xff0c;并對他做rbac授權。 apiVersion: v1 kind: ServiceAccount metadata:name: monitornamesp…

k8s-第九節-命名空間

命名空間 如果一個集群中部署了多個應用&#xff0c;所有應用都在一起&#xff0c;就不太好管理&#xff0c;也可以導致名字沖突等。 我們可以使用 namespace 把應用劃分到不同的命名空間&#xff0c;跟代碼里的 namespace 是一個概念&#xff0c;只是為了劃分空間。 # 創建命…

LeetCode熱題100刷題4:76. 最小覆蓋子串、239. 滑動窗口最大值、53. 最大子數組和、56. 合并區間

76. 最小覆蓋子串 滑動窗口解決字串問題。 labuladong的算法小抄中關于滑動窗口的算法總結&#xff1a; class Solution { public:string minWindow(string s, string t) {unordered_map<char,int> need,window;for(char c : t) {need[c];}int left 0, right 0;int …

2.8億東亞五國建筑數據分享

數據是GIS的血液&#xff01; 我們現在為你分享東亞5國的2.8億條建筑輪廓數據&#xff0c;該數據包括中國、日本、朝鮮、韓國和蒙古5個東亞國家完整、高質量的建筑物輪廓數據&#xff0c;你可以在文末查看領取方法。 數據介紹 雖然開源的全球的建筑數據已經有微軟的建筑數據…

elementUI中table組件固定列時會渲染兩次模板內容問題

今天在使用elementUI的table組件時&#xff0c;由于業務需要固定表格的前幾項列&#xff0c;然后獲取表格對象時發現竟然有兩個對象。 查閱資料發現&#xff0c;elementUI的固定列的實現原理是將兩個表格拼裝而成&#xff0c;因此獲取的對象也是兩個。對于需要使用對象的方法的…

vxe-table的序號一樣

使用vxe-table的時候&#xff0c;有的時候會出現序號相同的現象&#xff0c;這種現象一般出現在我們后面自己添加的行中&#xff0c;就像這種 此時的這三個序號是相同的&#xff0c;我來說一下原因&#xff0c;這是在添加新的一行的時候&#xff0c;有的時候數據很多&#xff0…

Mac 運行 Windows 軟件,Parallels Desktop 19和 CrossOver 24全面對比

Parallels Desktop 和 CrossOver 都是能滿足你「在 Mac 上運行 Windows 軟件」需求的工具。可能很多人都已經知道 Parallels Desktop 是「虛擬機」&#xff0c;但 CrossOver 其實并不是「虛擬機」。這兩款軟件有相同的作用&#xff0c;但由于實現原理的不同&#xff0c;兩者也有…

系統提示我未定義與 ‘double‘ 類型的輸入參數相對應的函數 ‘finverse‘,如何解決?

&#x1f3c6;本文收錄于「Bug調優」專欄&#xff0c;主要記錄項目實戰過程中的Bug之前因后果及提供真實有效的解決方案&#xff0c;希望能夠助你一臂之力&#xff0c;幫你早日登頂實現財富自由&#x1f680;&#xff1b;同時&#xff0c;歡迎大家關注&&收藏&&…

Kubernetes 部署簡單的應用

Kubernetes 部署簡單的應用 Kubernetes 是一個強大的容器編排平臺&#xff0c;它可以幫助我們自動化應用程序的部署、擴展和管理。在本期文章中&#xff0c;我們將學習如何使用 Kubernetes 部署一個簡單的應用程序。 1. 環境準備 確保你已經安裝了 Kubernetes 集群&#xff…

【python模塊】argparse

文章目錄 argparse模塊介紹基本用法add_argument() argparse模塊介紹 argparse 模塊是 Python 標準庫中的一個用于編寫用戶友好的命令行接口&#xff08;CLI&#xff09;的模塊。它允許程序定義它所需要的命令行參數&#xff0c;然后 argparse 會自動從 sys.argv 解析出那些參…

TCP粘包解決方法

一. 產生原因及解決方法 產生原因&#xff1a;TCP是面向連接、基于字節流的協議&#xff0c;其無邊界標記。當服務端處理速度比不其接收速度時&#xff0c;就很容易產生粘包現象。 解決方法&#xff1a;目前主要有兩種解決方法&#xff0c;一個是在內容中添加分割標識&#xf…