如何監(jiān)測(cè)Android網(wǎng)絡(luò)類(lèi)型:5G/4G/3G/2G能力
App可以通過(guò)了解所連接的網(wǎng)絡(luò)類(lèi)型來(lái)獲益,例如啟用某些功能需要5G提供的帶寬和低延遲。如果只有2G或3G網(wǎng)絡(luò)可用,加載時(shí)間會(huì)比較慢,因此我們可以對(duì)加載時(shí)間有一定的預(yù)期。
在這里,我們可以利用TelephonyManager類(lèi)來(lái)獲取各種關(guān)于移動(dòng)網(wǎng)絡(luò)狀態(tài)的信息,其中包括網(wǎng)絡(luò)類(lèi)型!不過(guò),使用TelephonyManager相當(dāng)復(fù)雜,因?yàn)椴煌腁ndroid版本有不同的情況需要考慮。
下面我提供了一個(gè)示例應(yīng)用程序,它可以檢測(cè)我們所連接的移動(dòng)網(wǎng)絡(luò)類(lèi)型,不僅僅是5G / 4G / 3G / 2G,還可以獲取到具體的子類(lèi)型。該應(yīng)用使用了TelephonyManager,并結(jié)合了Jetpack Compose、ViewModel和Kotlin Flow的編寫(xiě)方式。
使用TelephonyManager注冊(cè)以接收網(wǎng)絡(luò)信息更新
獲取TelephonyManager的方法如下:
valtelephonyManager= context.getSystemService(Context.TELEPHONY_SERVICE)asTelephonyManager
...當(dāng)上下文是一個(gè)Context實(shí)例時(shí)。請(qǐng)注意,一些手機(jī)具有多個(gè)SIM卡;如果您想查詢(xún)特定的SIM卡,請(qǐng)調(diào)用TelephonyManager實(shí)例上的.createForSubscriptionId(simCardNumber)。
使用這個(gè)實(shí)例,我們現(xiàn)在可以獲取網(wǎng)絡(luò)信息更新。所使用的過(guò)程取決于Android版本,即用戶(hù)的Android版本,而不是您應(yīng)用的目標(biāo)API級(jí)別。
Android ≥ 12(API ≥ 31)
Android 12及更高版本是最簡(jiǎn)單的情況,因?yàn)橛幸粋€(gè)專(zhuān)用的監(jiān)聽(tīng)器,并且不需要權(quán)限。
要注冊(cè)接收網(wǎng)絡(luò)類(lèi)型信息,我們使用registerTelephonyCallback(Executor, TelephonyCallback)方法,如下所示:
//ThethreadExecutorusedtorunthelistener.Thisgovernshowthreadsarecreatedand //reused.Hereweuseasinglethread. valexec=Executors.newSingleThreadExecutor() //Createthecallbackobject valcallback=object:TelephonyCallback(),TelephonyCallback.DisplayInfoListener{ overridefunonDisplayInfoChanged(telephonyDisplayInfo:TelephonyDisplayInfo){ //TODO:Thisisnext } } //Finally,registerthecallbacksoitcanstartreceivingresults. telephonyManager.registerTelephonyCallback(exec,callback)
注銷(xiāo)監(jiān)聽(tīng)器,方法如下:
telephonyManager.unregisterTelephonyCallback(callback)
"""
僅限 Android 11 (API 30)
注冊(cè)電話(huà)管理器回調(diào)的原始方法是使用 listen 方法。該方法接受各種類(lèi)型的監(jiān)聽(tīng)器;我們需要的是實(shí)現(xiàn)onDisplayInfoChanged接口的監(jiān)聽(tīng)器。
有趣的是,這個(gè)方法在一個(gè) Android 版本中就出現(xiàn)并消失了:
這需要READ_PHONE_STATE權(quán)限。我們將在 UI 代碼中稍后處理?,F(xiàn)在我們將繼續(xù),假定我們已經(jīng)擁有該權(quán)限。
//(Atthetopofthefile) @file:Suppress("DEPRECATION")//Suppressedasrequiredtosupportoldversion //SDK30usesTelephonyManager.listen()tolistenforTelephonyDisplayInfochanges. //ItrequiresREAD_PHONE_STATEpermission. @Suppress("OVERRIDE_DEPRECATION")//Suppressedasrequiredtosupportoldversion //Thisistheobjectthatwillreceivetheresults valcallback=object:PhoneStateListener(exec){ overridefunonDisplayInfoChanged(telephonyDisplayInfo:TelephonyDisplayInfo){ //TODO:Thisisnext } } //Startlisteningforresults telephonyManager.listen(callback,PhoneStateListener.LISTEN_DISPLAY_INFO_CHANGED)
注銷(xiāo)監(jiān)聽(tīng)使用下面代碼:
telephonyManager.listen(callback,0)
Android ≥ 7(API ≥ 24)
Android 10及以下沒(méi)有任何監(jiān)聽(tīng)網(wǎng)絡(luò)類(lèi)型變化的方法。要支持較舊的版本,您需要實(shí)施一個(gè)循環(huán),每隔幾秒主動(dòng)檢查。
檢查的代碼如下:
valnetworkType=telephonyManager.dataNetworkType
這需要READ_PHONE_STATE權(quán)限。
請(qǐng)注意,Android 10及以下版本不能支持5G,因?yàn)?G僅在Android 11及以上版本中可用。
返回的值:網(wǎng)絡(luò)類(lèi)型常量
在上述 Android 11 和 ≥12 的代碼中,會(huì)收到一個(gè)帶有TelephonyDisplayInfo對(duì)象的回調(diào)。該對(duì)象包含一個(gè)networkType和一個(gè)overrideNetworkType。而在 Android ≤10 的代碼中,只會(huì)收到一個(gè)networkType。
無(wú)論哪種情況,networkType可以是以下之一:
valbaseTypeString=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->"EVDOrev0" TelephonyManager.NETWORK_TYPE_EVDO_A->"EVDOrevA" TelephonyManager.NETWORK_TYPE_EVDO_B->"EVDOrevB" 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(newradio)5G" TelephonyManager.NETWORK_TYPE_TD_SCDMA->"TD_SCDMA" TelephonyManager.NETWORK_TYPE_UMTS->"UMTS" else->"[Unknown]" }
如果可用,overrideNetworkType會(huì)為某些類(lèi)型的4G和5G連接提供更多信息。以下是選項(xiàng):
valoverrideString=when(overrideNetworkType){ TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA->"5Gnon-standalone" TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED->"5Gstandalone(advanced)" TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_ADVANCED_PRO->"LTEAdvancedPro(5Ge)" TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_CA->"LTE(carrieraggregation)" else->null }
可能的null處理:
valnetTypeString=overrideString?:baseTypeString
將其構(gòu)建為一個(gè)Kotlin + Flows + ViewModel + Compose應(yīng)用程序
我在ViewModel中使用了一個(gè)KotlincallbackFlow來(lái)設(shè)置上述監(jiān)聽(tīng)器。如果您以前沒(méi)有遇到過(guò)callbackFlow,那就太棒了:它是一個(gè)流,可用于在外部API上創(chuàng)建一個(gè)監(jiān)聽(tīng)器,當(dāng)有人注冊(cè)時(shí),自動(dòng)移除該監(jiān)聽(tīng)器。
我使用.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), null)將callbackFlow從冷可觀察對(duì)象轉(zhuǎn)換為共享熱可觀察對(duì)象。這樣,如果有多個(gè)消費(fèi)者注冊(cè),就不會(huì)創(chuàng)建多個(gè)DisplayInfoListeners或PhoneStateListeners。WhileSubscribed(5000)部分確??捎^察對(duì)象在所有消費(fèi)者消失后仍保持存在一段時(shí)間,以防它們即將重新出現(xiàn)。(例如,在屏幕旋轉(zhuǎn)的情況下會(huì)發(fā)生這種情況)。
在Composable中,我使用collectAsStateWithLifecycle()來(lái)確保監(jiān)聽(tīng)器僅在應(yīng)用程序位于前臺(tái)時(shí)處于活動(dòng)狀態(tài)。
審核編輯:劉清
-
Android
+關(guān)注
關(guān)注
12文章
3935瀏覽量
127337 -
移動(dòng)網(wǎng)絡(luò)
+關(guān)注
關(guān)注
2文章
444瀏覽量
32849
原文標(biāo)題:如何監(jiān)測(cè)Android網(wǎng)絡(luò)類(lèi)型:5G/4G/3G/2G能力
文章出處:【微信號(hào):哆啦安全,微信公眾號(hào):哆啦安全】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論