网络状态变化时切换图片:BroadcastReceiver方案无效求助
我已经试过论坛上找到的多种解决方案,但都对我的情况无效。我的需求是在网络连接开启或关闭时切换界面上的图片,因此编写了一个继承自BroadcastReceiver的ConnectivityReceiver类,本希望它能在网络状态变化时通知主类,但目前没能正常工作。我的代码片段如下:
public class ConnectivityReceiver extends BroadcastReceiver { public static ConnectivityReceiverListener connectivityReceiverListener; public ConnectivityReceiver() { super(); } @Override public void onReceive(Context context, Intent intent) { // 这里本来要处理网络状态并通知主类,但好像没触发或者没传过去 } public interface ConnectivityReceiverListener { void onNetworkConnectionChanged(boolean isConnected); } }
下面是我总结的几个能解决这个问题的关键点,都是实际踩坑后验证有效的:
1. 别用静态注册,必须动态注册(Android 7.0+限制)
从Android 7.0开始,系统就禁止静态注册CONNECTIVITY_ACTION广播了,所以如果你的Receiver是在AndroidManifest里静态注册的,肯定收不到通知。正确的做法是在主Activity的onResume()里动态注册,onPause()里注销,避免内存泄漏:
@Override protected void onResume() { super.onResume(); IntentFilter filter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION); registerReceiver(new ConnectivityReceiver(), filter); } @Override protected void onPause() { super.onPause(); unregisterReceiver(new ConnectivityReceiver()); }
另外,别忘了在Manifest里添加必要的权限:
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.INTERNET" />
2. 确保回调接口正确绑定和调用
你的Receiver里定义了ConnectivityReceiverListener,但得让主类实现这个接口,并且把实例传给Receiver的静态变量,同时在onReceive里正确触发回调:
先补全Receiver的onReceive方法:
@Override public void onReceive(Context context, Intent intent) { ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo activeNetwork = cm.getActiveNetworkInfo(); boolean isConnected = activeNetwork != null && activeNetwork.isConnectedOrConnecting(); // 关键:调用回调通知主类 if (connectivityReceiverListener != null) { connectivityReceiverListener.onNetworkConnectionChanged(isConnected); } }
然后主Activity要实现接口并绑定Listener:
public class MainActivity extends AppCompatActivity implements ConnectivityReceiver.ConnectivityReceiverListener { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // 绑定Listener,让Receiver能找到主类 ConnectivityReceiver.connectivityReceiverListener = this; } @Override public void onNetworkConnectionChanged(boolean isConnected) { // 在这里完成图片切换,注意要在主线程操作 ImageView networkImg = findViewById(R.id.iv_network_status); if (isConnected) { networkImg.setImageResource(R.drawable.ic_network_connected); } else { networkImg.setImageResource(R.drawable.ic_network_disconnected); } } // 别忘了前面的动态注册/注销代码 }
3. 高版本系统推荐用NetworkCallback替代BroadcastReceiver
Android 10及以上,CONNECTIVITY_ACTION广播的触发逻辑有很多限制,官方更推荐用ConnectivityManager.NetworkCallback来监听网络状态,这个方式更稳定:
private ConnectivityManager.NetworkCallback networkCallback = new ConnectivityManager.NetworkCallback() { @Override public void onAvailable(Network network) { super.onAvailable(network); // 网络连接成功,切图 runOnUiThread(() -> { ImageView img = findViewById(R.id.iv_network_status); img.setImageResource(R.drawable.ic_network_connected); }); } @Override public void onLost(Network network) { super.onLost(network); // 网络断开,切图 runOnUiThread(() -> { ImageView img = findViewById(R.id.iv_network_status); img.setImageResource(R.drawable.ic_network_disconnected); }); } }; // 注册回调 @Override protected void onResume() { super.onResume(); ConnectivityManager cm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); cm.registerNetworkCallback( new NetworkRequest.Builder().build(), networkCallback ); } // 注销回调 @Override protected void onPause() { super.onPause(); ConnectivityManager cm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); cm.unregisterNetworkCallback(networkCallback); }
这种方式不需要BroadcastReceiver,直接通过系统回调就能拿到网络状态变化,比广播更可靠。
4. 注意UI操作必须在主线程
不管用哪种方式,切换图片是UI操作,一定要确保在主线程执行。虽然onReceive默认是在主线程,但如果Receiver是单独进程的话就会出问题,所以保险起见,用runOnUiThread或者Handler把操作切到主线程,避免崩溃。
你可以对照这几点检查自己的代码,应该能解决Receiver无法通知主类的问题。
内容的提问来源于stack exchange,提问作者DeimosPhx

