如何監(jiān)測Android網(wǎng)絡(luò)類型:5G/4G/3G/2G能力
App可以通過了解所連接的網(wǎng)絡(luò)類型來獲益,例如啟用某些功能需要5G提供的帶寬和低延遲。如果只有2G或3G網(wǎng)絡(luò)可用,加載時(shí)間會(huì)比較慢,因此我們可以對加載時(shí)間有一定的預(yù)期。
在這里,我們可以利用TelephonyManager類來獲取各種關(guān)于移動(dòng)網(wǎng)絡(luò)狀態(tài)的信息,其中包括網(wǎng)絡(luò)類型!不過,使用TelephonyManager相當(dāng)復(fù)雜,因?yàn)椴煌腁ndroid版本有不同的情況需要考慮。
下面我提供了一個(gè)示例應(yīng)用程序,它可以檢測我們所連接的移動(dòng)網(wǎng)絡(luò)類型,不僅僅是5G / 4G / 3G / 2G,還可以獲取到具體的子類型。該應(yīng)用使用了TelephonyManager
,并結(jié)合了Jetpack Compose、ViewModel和Kotlin Flow的編寫方式。
https://github.com/tdcolvin/NetworkTypeDetector文章來源地址http://www.zghlxwxcb.cn/news/detail-501301.html
使用TelephonyManager注冊以接收網(wǎng)絡(luò)信息更新
獲取TelephonyManager
的方法如下:
val telephonyManager =
context.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager
…當(dāng)上下文是一個(gè)Context實(shí)例時(shí)。請注意,一些手機(jī)具有多個(gè)SIM卡;如果您想查詢特定的SIM卡,請調(diào)用TelephonyManager
實(shí)例上的.createForSubscriptionId(simCardNumber)
。
使用這個(gè)實(shí)例,我們現(xiàn)在可以獲取網(wǎng)絡(luò)信息更新。所使用的過程取決于Android版本,即用戶的Android版本,而不是您應(yīng)用的目標(biāo)API級別。
Android ≥ 12(API ≥ 31)
Android 12及更高版本是最簡單的情況,因?yàn)橛幸粋€(gè)專用的監(jiān)聽器,并且不需要權(quán)限。
要注冊接收網(wǎng)絡(luò)類型信息,我們使用registerTelephonyCallback(Executor, TelephonyCallback)
方法,如下所示:
// The thread Executor used to run the listener. This governs how threads are created and
// reused. Here we use a single thread.
val exec = Executors.newSingleThreadExecutor()
// Create the callback object
val callback = object : TelephonyCallback(), TelephonyCallback.DisplayInfoListener {
override fun onDisplayInfoChanged(telephonyDisplayInfo: TelephonyDisplayInfo) {
//TODO: This is next
}
}
// Finally, register the callback so it can start receiving results.
telephonyManager.registerTelephonyCallback(exec, callback)
注銷監(jiān)聽器,方法如下:
telephonyManager.unregisterTelephonyCallback(callback)
“”"
僅限 Android 11 (API 30)
注冊電話管理器回調(diào)的原始方法是使用 listen 方法。該方法接受各種類型的監(jiān)聽器;我們需要的是實(shí)現(xiàn) onDisplayInfoChanged
接口的監(jiān)聽器。
有趣的是,這個(gè)方法在一個(gè) Android 版本中就出現(xiàn)并消失了:
這需要 READ_PHONE_STATE
權(quán)限。我們將在 UI 代碼中稍后處理。現(xiàn)在我們將繼續(xù),假定我們已經(jīng)擁有該權(quán)限。
// (At the top of the file)
@file:Suppress("DEPRECATION") //Suppressed as required to support old version
// SDK 30 uses TelephonyManager.listen() to listen for TelephonyDisplayInfo changes.
// It requires READ_PHONE_STATE permission.
@Suppress("OVERRIDE_DEPRECATION") //Suppressed as required to support old version
// This is the object that will receive the results
val callback = object : PhoneStateListener(exec) {
override fun onDisplayInfoChanged(telephonyDisplayInfo: TelephonyDisplayInfo) {
//TODO: This is next
}
}
// Start listening for results
telephonyManager.listen(callback, PhoneStateListener.LISTEN_DISPLAY_INFO_CHANGED)
注銷監(jiān)聽使用下面代碼:
telephonyManager.listen(callback, 0)
Android ≥ 7(API ≥ 24)
Android 10及以下沒有任何監(jiān)聽網(wǎng)絡(luò)類型變化的方法。要支持較舊的版本,您需要實(shí)施一個(gè)循環(huán),每隔幾秒主動(dòng)檢查。
檢查的代碼如下:
val networkType = telephonyManager.dataNetworkType
這需要 READ_PHONE_STATE
權(quán)限。
請注意,Android 10及以下版本不能支持5G,因?yàn)?G僅在Android 11及以上版本中可用。
返回的值:網(wǎng)絡(luò)類型常量
在上述 Android 11 和 ≥12 的代碼中,會(huì)收到一個(gè)帶有TelephonyDisplayInfo
對象的回調(diào)。該對象包含一個(gè) networkType
和一個(gè) overrideNetworkType
。而在 Android ≤10 的代碼中,只會(huì)收到一個(gè) networkType
。
無論哪種情況,networkType
可以是以下之一:
val baseTypeString = when(networkType) {
TelephonyManager.NETWORK_TYPE_CDMA -> "CDMA"
TelephonyManager.NETWORK_TYPE_1xRTT -> "1xRTT"
TelephonyManager.NETWORK_TYPE_EDGE -> "EDGE"
TelephonyManager.NETWORK_TYPE_EHRPD -> "eHRPD"
TelephonyManager.NETWORK_TYPE_EVDO_0 -> "EVDO rev 0"
TelephonyManager.NETWORK_TYPE_EVDO_A -> "EVDO rev A"
TelephonyManager.NETWORK_TYPE_EVDO_B -> "EVDO rev B"
TelephonyManager.NETWORK_TYPE_GPRS -> "GPRS"
TelephonyManager.NETWORK_TYPE_GSM -> "GSM"
TelephonyManager.NETWORK_TYPE_HSDPA -> "HSDPA"
TelephonyManager.NETWORK_TYPE_HSPA -> "HSPA"
TelephonyManager.NETWORK_TYPE_HSPAP -> "HSPA+"
TelephonyManager.NETWORK_TYPE_HSUPA -> "HSUPA"
TelephonyManager.NETWORK_TYPE_IDEN -> "iDen"
TelephonyManager.NETWORK_TYPE_IWLAN -> "IWLAN"
TelephonyManager.NETWORK_TYPE_LTE -> "LTE"
TelephonyManager.NETWORK_TYPE_NR -> "NR (new radio) 5G"
TelephonyManager.NETWORK_TYPE_TD_SCDMA -> "TD_SCDMA"
TelephonyManager.NETWORK_TYPE_UMTS -> "UMTS"
else -> "[Unknown]"
}
如果可用,overrideNetworkType
會(huì)為某些類型的4G和5G連接提供更多信息。以下是選項(xiàng):
val overrideString = when(overrideNetworkType) {
TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA -> "5G non-standalone"
TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED -> "5G standalone (advanced)"
TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_ADVANCED_PRO -> "LTE Advanced Pro (5Ge)"
TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_CA -> "LTE (carrier aggregation)"
else -> null
}
可能的null處理:
val netTypeString = overrideString ?: baseTypeString
將其構(gòu)建為一個(gè)Kotlin + Flows + ViewModel + Compose應(yīng)用程序
我在ViewModel中使用了一個(gè)Kotlin callbackFlow
來設(shè)置上述監(jiān)聽器。如果您以前沒有遇到過callbackFlow
,那就太棒了:它是一個(gè)流,可用于在外部API上創(chuàng)建一個(gè)監(jiān)聽器,當(dāng)有人注冊時(shí),自動(dòng)移除該監(jiān)聽器。
我使用.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), null)
將callbackFlow
從冷可觀察對象轉(zhuǎn)換為共享熱可觀察對象。這樣,如果有多個(gè)消費(fèi)者注冊,就不會(huì)創(chuàng)建多個(gè)DisplayInfoListeners
或PhoneStateListeners
。WhileSubscribed(5000)
部分確??捎^察對象在所有消費(fèi)者消失后仍保持存在一段時(shí)間,以防它們即將重新出現(xiàn)。(例如,在屏幕旋轉(zhuǎn)的情況下會(huì)發(fā)生這種情況)。
在Composable中,我使用collectAsStateWithLifecycle()
來確保監(jiān)聽器僅在應(yīng)用程序位于前臺(tái)時(shí)處于活動(dòng)狀態(tài)。文章來源:http://www.zghlxwxcb.cn/news/detail-501301.html
GitHub
https://github.com/tdcolvin/NetworkTypeDetector
到了這里,關(guān)于如何監(jiān)測Android網(wǎng)絡(luò)類型:5G/4G/3G/2G能力的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!