8. WiFi热点信息获取:获取当前连接WiFi的SSID、BSSID、IP地址、链路速度、信号强度(RSSI)、频率
做WiFi开发,第一步要干什么?
我个人习惯,先搞清楚当前连的是哪个热点。就像你打电话,总得知道对方是谁吧?
这一章,我们就来聊聊怎么从Android系统里,把这些信息“掏”出来。
8.1 我们能拿到哪些信息?
先列个清单,看看Android到底给了我们什么。
| 信息项 | 含义 | 典型值 |
|---|---|---|
| SSID | WiFi的名字,你看到的那个“我家WiFi” | "MyHome_5G" |
| BSSID | 路由器的MAC地址,唯一标识 | "a4:5e:60:xx:xx:xx" |
| IP地址 | 手机在当前局域网内的地址 | "192.168.1.101" |
| 链路速度 | 当前连接的理论最大速率,单位Mbps | 866 |
| 信号强度(RSSI) | 接收信号强度,单位dBm | -55 |
| 频率 | 工作在2.4GHz还是5GHz | 5180 (MHz) |
这些信息,说白了就是WiFi的“身份证”和“体检报告”。
8.2 核心API:WifiManager 和 WifiInfo
获取这些信息,主要靠两个类:WifiManager 和 WifiInfo。
WifiManager 是系统WiFi服务的管理员,WifiInfo 就是当前连接的详细档案。
获取 WifiManager 实例的代码,我猜你已经很熟了:
WifiManager wifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
然后,拿到 WifiInfo:
WifiInfo wifiInfo = wifiManager.getConnectionInfo();
嗯,这里要注意。从Android 10开始,系统对WiFi信息的访问权限收紧了。如果你直接这么拿,可能会拿到 null 或者假的SSID(比如一堆0)。
从Android 10 (API 29) 开始,获取SSID和BSSID需要 ACCESS_FINE_LOCATION 权限,并且需要用户开启位置服务。否则,getSSID() 会返回 "<unknown ssid>"。
我曾经在一个项目中,忘了处理这个情况,结果用户反馈说“WiFi名字全是乱码”。排查了半天,才发现是权限没到位。
8.3 逐个击破:获取每一项信息
拿到 WifiInfo 对象后,事情就简单了。每个信息都有对应的getter方法。
8.3.1 SSID 和 BSSID
String ssid = wifiInfo.getSSID();
String bssid = wifiInfo.getBSSID();
注意,getSSID() 返回的字符串可能带双引号,比如 "\"MyHome\""。你想想看,这多坑。所以拿到后最好处理一下:
if (ssid != null && ssid.startsWith("\"") && ssid.endsWith("\"")) {
ssid = ssid.substring(1, ssid.length() - 1);
}
8.3.2 IP地址
这个有点绕。getIpAddress() 返回的是一个int值,不是我们常见的点分十进制字符串。
int ipInt = wifiInfo.getIpAddress();
String ipString = String.format("%d.%d.%d.%d",
(ipInt & 0xff),
(ipInt >> 8 & 0xff),
(ipInt >> 16 & 0xff),
(ipInt >> 24 & 0xff)
);
为什么Android要这么设计?我猜是为了效率。但对我们开发者来说,确实不太友好。
8.3.3 链路速度
int linkSpeed = wifiInfo.getLinkSpeed(); // 单位 Mbps
这个值代表的是当前连接的理论最大速率。实际传输速度通常会低一些。我在测试时发现,866Mbps的链接,实际跑文件传输也就400-500Mbps左右。
8.3.4 信号强度 (RSSI)
int rssi = wifiInfo.getRssi(); // 单位 dBm
RSSI是负数,值越大(越接近0)信号越好。比如 -30dBm 就是满格,-80dBm 基本就快断了。
Android还提供了一个工具方法,可以把RSSI换算成我们常见的信号格数:
int level = WifiManager.calculateSignalLevel(rssi, 5); // 5格信号
RSSI受环境影响很大。你拿着手机转个身,数值可能就跳变10个dBm。所以做信号监测时,我建议做个滑动平均,别直接用原始值。
8.3.5 频率
int frequency = wifiInfo.getFrequency(); // 单位 MHz
通过频率,我们可以判断是2.4G还是5G:
boolean is5GHz = frequency > 4900 && frequency < 5900;
boolean is24GHz = frequency > 2400 && frequency < 2500;
为什么不用 is5GHz() 方法?因为那个方法在API 21才加入,而且有些厂商实现有问题。我自己更习惯用频率范围来判断,兼容性更好。
8.4 完整代码示例
把上面这些串起来,就是一个完整的工具类:
public class WifiInfoHelper {
public static WifiInfoBean getCurrentWifiInfo(Context context) {
WifiManager wifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
if (wifiManager == null) return null;
WifiInfo wifiInfo = wifiManager.getConnectionInfo();
if (wifiInfo == null) return null;
WifiInfoBean bean = new WifiInfoBean();
// SSID
String ssid = wifiInfo.getSSID();
if (ssid != null && ssid.startsWith("\"") && ssid.endsWith("\"")) {
ssid = ssid.substring(1, ssid.length() - 1);
}
bean.ssid = ssid;
// BSSID
bean.bssid = wifiInfo.getBSSID();
// IP地址
int ipInt = wifiInfo.getIpAddress();
bean.ipAddress = String.format("%d.%d.%d.%d",
(ipInt & 0xff),
(ipInt >> 8 & 0xff),
(ipInt >> 16 & 0xff),
(ipInt >> 24 & 0xff)
);
// 链路速度
bean.linkSpeed = wifiInfo.getLinkSpeed();
// RSSI
bean.rssi = wifiInfo.getRssi();
// 频率
bean.frequency = wifiInfo.getFrequency();
return bean;
}
public static class WifiInfoBean {
public String ssid;
public String bssid;
public String ipAddress;
public int linkSpeed;
public int rssi;
public int frequency;
}
}
8.5 知识体系图
为了让你更直观地理解整个流程,我画了一张图:
8.6 避坑指南
最后,分享几个我踩过的坑:
- 权限问题:Android 10以上,别忘了申请
ACCESS_FINE_LOCATION,并且引导用户开启位置开关。 - 空指针:WiFi没连接时,
getConnectionInfo()返回的对象里,很多字段是无效值。比如SSID可能是null,RSSI可能是-127。记得做判空。 - 线程问题:这些API调用很快,但如果你在子线程里操作,记得确保
Context是Application级别的,避免内存泄漏。 - 厂商差异:有些国产手机对
getFrequency()的实现有bug,返回0。遇到这种情况,我一般会加个兜底逻辑,从扫描结果里再查一次。
好了,这一章的内容就到这里。信息获取是WiFi开发的基础,把这些搞扎实了,后面的操作才能得心应手。
公众号:蓝海资料掘金营,微信deep3321