Android Kotlin实现类系统信号塔样式的网络信号强度计算与展示
Android Kotlin 实现对齐系统规则的移动网络信号强度展示方案
前置权限配置
首先在AndroidManifest.xml中添加必要权限:
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <!-- Android 12+ 获取蜂窝信号强度强制要求该权限,否则返回无效值 --> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <!-- Android 9及以下版本兼容权限 --> <uses-permission android:name="android.permission.READ_PHONE_STATE" />
Android 6.0(API 23)及以上版本需要在页面运行时动态申请ACCESS_FINE_LOCATION权限,用户授权后才能正常获取信号数据,未授权状态下所有信号接口返回默认无效值。
核心信号监听实现
Android 10(API 29)及以上推荐使用TelephonyCallback实现信号监听,低版本兼容旧版PhoneStateListener即可,信号计算逻辑完全复用。
信号格数判定完全对齐AOSP原生规则,共分5档(0~4格,0格为无信号,4格为满格),不同网络制式的判定阈值如下:
- 2G(GSM):基于RSSI值(单位dBm)
- ≤-105dBm:0格
- -104~-99dBm:1格
- -98~-93dBm:2格
- -92~-85dBm:3格
- ≥-84dBm:4格
- 4G(LTE):基于RSRP值(单位dBm)
- ≤-125dBm:0格
- -124~-115dBm:1格
- -114~-105dBm:2格
- -104~-95dBm:3格
- ≥-94dBm:4格
- 5G(NR):基于SSRSRP值(单位dBm)
- ≤-120dBm:0格
- -119~-110dBm:1格
- -109~-100dBm:2格
- -99~-90dBm:3格
- ≥-89dBm:4格
核心实现代码如下:
private lateinit var telephonyManager: TelephonyManager private var signalCallback: TelephonyCallback? = null // 在页面onStart生命周期中启动监听 @RequiresApi(Build.VERSION_CODES.S) fun startSignalMonitor() { telephonyManager = getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager signalCallback = object : TelephonyCallback(), TelephonyCallback.SignalStrengthsListener { override fun onSignalStrengthsChanged(signalStrength: SignalStrength) { val currentLevel = getMatchSystemSignalLevel(signalStrength) // 拿到格数后直接更新UI即可 refreshSignalUi(currentLevel, signalStrength) } } telephonyManager.registerTelephonyCallback(mainExecutor, signalCallback!!) } // 优先复用系统计算结果,保证和状态栏显示一致,兜底逻辑适配异常ROM private fun getMatchSystemSignalLevel(signalStrength: SignalStrength): Int { val systemCalcLevel = signalStrength.level if (systemCalcLevel in 0..4) return systemCalcLevel // 系统返回无效值时用自定义阈值兜底 var targetLevel = 0 signalStrength.cellSignalStrengths.forEach { cellInfo -> val dbmValue = cellInfo.dbm targetLevel = when { cellInfo.javaClass.name.contains("GsmSignalStrength") -> calcGsmLevel(dbmValue) cellInfo.javaClass.name.contains("LteSignalStrength") -> calcLteLevel(dbmValue) cellInfo.javaClass.name.contains("NrSignalStrength") -> calcNrLevel(dbmValue) else -> cellInfo.level } } return targetLevel.coerceIn(0, 4) } // 各制式兜底格数计算 private fun calcGsmLevel(dbm: Int): Int = when { dbm >= -84 -> 4 dbm >= -92 -> 3 dbm >= -98 -> 2 dbm >= -104 -> 1 else -> 0 } private fun calcLteLevel(dbm: Int): Int = when { dbm >= -94 -> 4 dbm >= -104 -> 3 dbm >= -114 -> 2 dbm >= -124 -> 1 else -> 0 } private fun calcNrLevel(dbm: Int): Int = when { dbm >= -89 -> 4 dbm >= -99 -> 3 dbm >= -109 -> 2 dbm >= -119 -> 1 else -> 0 } // 页面onStop生命周期中注销监听,避免内存泄漏 fun stopSignalMonitor() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { signalCallback?.let { telephonyManager.unregisterTelephonyCallback(it) } } }
UI适配说明
- 图标资源可直接复用系统内置的信号塔素材,也可自行准备04格对应状态的切图:0格展示带断开标识的无信号图标,14格分别展示对应格数的信号塔图标即可,和系统状态栏逻辑完全一致。
- 信号数值展示直接取当前驻网小区的
dbm值即可,单位为dBm,注意该值为负数,绝对值越小代表信号质量越好,不要转成正数展示造成用户误解。 - 双卡场景下需要监听指定SIM卡信号时,调用
telephonyManager.createForSubscriptionId(对应卡的subId)生成指定卡的TelephonyManager实例即可,后续逻辑和单卡完全一致。
避坑提示
- 不要调用
WifiManager相关的信号获取接口,该接口仅能获取WiFi信号强度,和蜂窝移动网络无关。 - Android 9及以下版本无法使用
TelephonyCallback时,替换为旧版PhoneStateListener的LISTEN_SIGNAL_STRENGTHS回调即可,信号计算逻辑不需要修改。 - 优先使用系统自带的
SignalStrength.getLevel()返回值,不要全量走自定义阈值计算——部分国产定制ROM会调整信号格数的判定规则,直接用系统计算结果能保证App内显示的格数和系统状态栏完全对齐,不会出现显示不一致的问题。
内容的提问来源于stack exchange,提问作者UserSynergy
相关产品推荐
相关产品推荐

