9、WiFi信号强度与图标:RSSI与dBm、计算信号等级、自定义WiFi信号图标

做Android WiFi开发,信号强度这块是绕不开的。你想想看,用户打开WiFi列表,第一眼看到的就是那个小扇形的图标——满格还是半格,直接决定了ta会不会点进去连接。

但说实话,这个信号强度的计算,远没有表面上看起来那么简单。我早期做项目时,就踩过不少坑。今天咱们把RSSI、dBm、信号等级计算,还有自定义图标这些事,一次性说清楚。

9.1 RSSI与dBm:信号强度的两个面孔

先搞清楚两个概念:RSSI(Received Signal Strength Indicator)和dBm(分贝毫瓦)。

RSSI是个相对值,不同芯片厂商的算法不一样。有的芯片RSSI范围是0-255,有的只有0-60。说白了,RSSI就是个“自家尺子”,没法跨设备比较。

而dBm是绝对功率单位,0dBm对应1毫瓦。数值越大(越接近0),信号越强。比如-30dBm表示信号非常好,-90dBm基本就断连了。

Android系统里,ScanResult.level返回的就是dBm值。我习惯直接用这个值做判断,因为它才是标准化的。

关键点:RSSI是芯片原始值,dBm是换算后的标准值。开发时请用dBm。

9.2 信号等级计算:从dBm到格数

系统怎么把-75dBm变成两格信号?靠的是calculateSignalLevel()方法。

Android提供了WifiManager.calculateSignalLevel(int rssi, int numLevels),传入dBm值和你要分的等级数(通常是4或5),返回0到numLevels-1的整数。

// 示例:将dBm转换为5级信号
WifiManager wifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE);
int rssi = -75; // 假设扫描到的信号强度
int numLevels = 5;
int level = WifiManager.calculateSignalLevel(rssi, numLevels);
// level 返回 0-4,0最弱,4最强

内部是怎么算的?其实很简单:

  • 定义信号最差值(比如-100dBm)和最优值(比如-55dBm)
  • 把区间等分,看当前值落在哪个段

我曾经在项目里发现,某些国产手机把最优值改成了-50dBm,导致同样的-70dBm,在A手机上显示3格,在B手机上显示2格。嗯,这就是厂商定制带来的坑。

我的建议:如果你需要跨设备一致的信号显示,最好自己实现一套等级算法,不要完全依赖系统API。

9.3 自定义WiFi信号图标:从系统到个性

系统自带的WiFi图标,说实话,样式比较固定。如果你想做差异化,比如游戏模式用炫酷图标,老人模式用超大图标,那就得自己动手了。

实现思路有两种:

  1. 用不同图片表示不同等级——简单粗暴,但图片多,适配麻烦
  2. 用代码动态绘制——灵活,但需要懂Canvas

我个人推荐第二种。下面是一个简单的自定义View,根据信号等级绘制扇形图标:

public class WifiSignalView extends View {
    private int signalLevel = 0; // 0-4
    private Paint paint;
    private int activeColor = Color.parseColor("#4CAF50");
    private int inactiveColor = Color.parseColor("#BDBDBD");

    public WifiSignalView(Context context, AttributeSet attrs) {
        super(context, attrs);
        paint = new Paint(Paint.ANTI_ALIAS_FLAG);
    }

    public void setSignalLevel(int level) {
        this.signalLevel = Math.max(0, Math.min(4, level));
        invalidate();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        int width = getWidth();
        int height = getHeight();
        int centerX = width / 2;
        int centerY = height - 20; // 底部留白

        // 画4个弧,从内到外
        for (int i = 0; i < 4; i++) {
            paint.setColor(i < signalLevel ? activeColor : inactiveColor);
            float radius = 20 + i * 25;
            RectF rect = new RectF(centerX - radius, centerY - radius,
                                   centerX + radius, centerY + radius);
            canvas.drawArc(rect, -135, 90, true, paint);
        }
    }
}

这段代码画了4个同心弧,每个弧代表一格信号。颜色根据等级变化,未激活的用灰色。

注意:自定义View时,记得处理padding和wrap_content。我早期就吃过这个亏,图标被截了一半。

9.4 知识体系总览

下面这张图,把信号强度相关的核心逻辑串起来了:

WiFi信号强度知识体系 ScanResult.level RSSI(芯片原始值) dBm(标准功率值) 厂商自定义算法 calculateSignalLevel() 信号等级(0-4格)→ 自定义图标

9.5 实战中的避坑指南

最后分享几个我踩过的坑:

  • 不要直接拿RSSI做比较——不同芯片的RSSI范围不同,跨设备对比没意义。用dBm。
  • 信号等级刷新不要太频繁——WiFi扫描间隔建议至少3秒,否则图标闪个不停,用户会疯的。
  • 注意负值比较——-60dBm比-80dBm大,但-60 > -80。写条件判断时别搞反了。
  • 自定义图标要考虑无障碍——颜色区分对色盲用户不友好,建议同时用形状或文字辅助。

我曾经在一个项目中,直接用了系统API的calculateSignalLevel,结果在某个定制ROM上,信号永远显示满格。查了半天,发现厂商把最差值改成了-200dBm。从那以后,我都是自己写等级计算逻辑,稳得很。

好了,信号强度这块就聊到这。记住:dBm是王道,自定义图标是加分项,踩坑经验是无价之宝。

公众号:蓝海资料掘金营,微信deep3321