27、WiFi广播与动态注册:监听WiFi状态变化
做Android WiFi开发,绕不开一个核心机制——广播。说白了,系统把WiFi的状态变化通过广播告诉你,你收到后做出响应。比如WiFi开关打开了、连接上了一个热点、信号变弱了……这些都是通过广播传递的。
我个人习惯用动态注册的方式监听这些广播。为什么?因为灵活,而且能避免很多坑。今天我们就来聊聊WiFi相关的两个核心广播,以及如何正确地注册和解注册。
两个核心广播:WIFI_STATE_CHANGED_ACTION 和 NETWORK_STATE_CHANGED_ACTION
Android系统定义了很多WiFi相关的广播,但日常开发中,这两个用得最多:
| 广播Action | 含义 | 携带的Extra信息 |
|---|---|---|
WIFI_STATE_CHANGED_ACTION |
WiFi硬件状态变化(开启/关闭/错误等) | EXTRA_WIFI_STATE:新状态EXTRA_PREVIOUS_WIFI_STATE:旧状态 |
NETWORK_STATE_CHANGED_ACTION |
WiFi网络连接状态变化(连接/断开/认证中) | EXTRA_NETWORK_INFO:NetworkInfo对象EXTRA_BSSID:当前连接的BSSID |
嗯,这里要注意:WIFI_STATE_CHANGED_ACTION 是告诉你WiFi模块本身的状态,比如用户去设置里打开了WiFi开关。而 NETWORK_STATE_CHANGED_ACTION 是告诉你网络连接的情况,比如连上了某个路由器,或者断开了。
我在项目中遇到过一个问题:有同事只监听 NETWORK_STATE_CHANGED_ACTION,结果WiFi开关关闭时,他收不到任何通知,导致UI一直显示“已连接”。这就是没搞清楚两个广播的区别。
动态注册 vs 静态注册
Android的广播注册有两种方式:
- 静态注册:在AndroidManifest.xml里声明。从Android 8.0开始,大部分隐式广播都不支持静态注册了。WiFi相关的广播正好属于被限制的那一类。
- 动态注册:在代码里通过
registerReceiver注册,在onPause或onDestroy里解注册。
说白了,现在做WiFi开发,你只能用动态注册。静态注册不仅可能收不到广播,还会被系统直接忽略。我建议你养成习惯——所有WiFi广播都用动态注册。
动态注册的完整流程
流程其实很简单,就三步:
- 创建
BroadcastReceiver子类,实现onReceive方法。 - 在合适的时机(比如
onResume)调用registerReceiver。 - 在合适的时机(比如
onPause)调用unregisterReceiver。
你想想看,如果不解注册会怎样?Activity销毁了但Receiver还在,系统会一直持有你的Context引用,导致内存泄漏。我曾经排查过一个内存泄漏问题,查了半天,最后发现就是广播没解注册。
代码示例:监听WiFi状态变化
下面是一个完整的示例,演示如何动态注册并监听WiFi开关状态和网络连接状态:
public class WifiBroadcastReceiver extends BroadcastReceiver {
private static final String TAG = "WifiReceiver";
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action == null) return;
// 处理WiFi开关状态变化
if (WifiManager.WIFI_STATE_CHANGED_ACTION.equals(action)) {
int wifiState = intent.getIntExtra(
WifiManager.EXTRA_WIFI_STATE,
WifiManager.WIFI_STATE_UNKNOWN
);
handleWifiStateChanged(wifiState);
}
// 处理网络连接状态变化
if (WifiManager.NETWORK_STATE_CHANGED_ACTION.equals(action)) {
NetworkInfo networkInfo = intent.getParcelableExtra(
WifiManager.EXTRA_NETWORK_INFO
);
if (networkInfo != null) {
handleNetworkStateChanged(networkInfo);
}
}
}
private void handleWifiStateChanged(int state) {
switch (state) {
case WifiManager.WIFI_STATE_ENABLED:
Log.d(TAG, "WiFi已开启");
break;
case WifiManager.WIFI_STATE_DISABLED:
Log.d(TAG, "WiFi已关闭");
break;
case WifiManager.WIFI_STATE_ENABLING:
Log.d(TAG, "WiFi正在开启...");
break;
case WifiManager.WIFI_STATE_DISABLING:
Log.d(TAG, "WiFi正在关闭...");
break;
default:
Log.d(TAG, "WiFi状态未知: " + state);
break;
}
}
private void handleNetworkStateChanged(NetworkInfo networkInfo) {
NetworkInfo.State state = networkInfo.getState();
if (state == NetworkInfo.State.CONNECTED) {
Log.d(TAG, "WiFi已连接");
} else if (state == NetworkInfo.State.DISCONNECTED) {
Log.d(TAG, "WiFi已断开");
} else if (state == NetworkInfo.State.CONNECTING) {
Log.d(TAG, "WiFi正在连接...");
}
}
}
在Activity中注册和解注册
有了Receiver,接下来就是在Activity里管理它的生命周期:
public class MainActivity extends AppCompatActivity {
private WifiBroadcastReceiver wifiReceiver;
private IntentFilter wifiIntentFilter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 创建Receiver实例
wifiReceiver = new WifiBroadcastReceiver();
// 创建IntentFilter,添加要监听的Action
wifiIntentFilter = new IntentFilter();
wifiIntentFilter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
wifiIntentFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
}
@Override
protected void onResume() {
super.onResume();
// 动态注册广播
registerReceiver(wifiReceiver, wifiIntentFilter);
}
@Override
protected void onPause() {
super.onPause();
// 解注册广播,防止内存泄漏
unregisterReceiver(wifiReceiver);
}
}
核心要点:
- 注册和解注册必须成对出现,推荐在
onResume和onPause中操作。 - 不要在
onCreate里注册、onDestroy里解注册。因为Activity可能被重建,但onDestroy不一定被调用。 - 如果Receiver是内部类,记得声明为
static,避免持有外部Activity的引用。
避坑指南:我曾经踩过的坑
我曾经在项目里犯过一个低级错误:在 onCreate 里注册了广播,但忘记在 onPause 里解注册。结果用户旋转屏幕时,Activity重建了多次,Receiver也被注册了多次。每次收到广播,onReceive 就被调用多次,导致UI闪烁、数据重复处理。
还有一个坑:NETWORK_STATE_CHANGED_ACTION 在Android 10及以上版本中,EXTRA_NETWORK_INFO 已经被标记为废弃。虽然还能用,但Google推荐使用 ConnectivityManager.NetworkCallback 来监听网络变化。不过对于WiFi专项开发,这个广播仍然是最直接的方式。
小技巧:如果你只需要监听WiFi连接状态,可以只注册 NETWORK_STATE_CHANGED_ACTION。但如果你需要知道WiFi开关是否打开(比如在开关动画时做UI反馈),那就必须同时监听 WIFI_STATE_CHANGED_ACTION。
知识体系总览
下面这张图帮你理清本章的核心逻辑:
总结一下
动态注册WiFi广播,说白了就是三步:创建Receiver、注册、解注册。核心是理解两个广播的区别——一个管硬件状态,一个管网连接状态。生命周期管理是重中之重,我建议你每次写广播相关代码时,都先问自己一句:“解注册写了吗?”
嗯,这一章的内容就到这里。代码不多,但背后的原理和坑不少。你动手写一遍,印象会更深刻。