如何在BroadcastReceiver中打开WiFi设置并实现联网提示弹窗
嘿,我来帮你搞定这个问题!你的BroadcastReceiver思路没问题,但有两个核心问题需要调整:直接用ApplicationContext弹AlertDialog会崩溃,以及还没实现跳转到WiFi设置的逻辑。下面一步步给你修正:
1. 修复AlertDialog的Context问题
BroadcastReceiver的onReceive里拿到的context是Application Context,而AlertDialog需要依附于Activity的Context才能正常显示。如果直接用这个context弹Dialog,会抛出android.view.WindowManager$BadTokenException异常。
解决办法是给Dialog的Window设置合适的类型,或者如果你的Receiver是动态注册在某个Activity里的,直接传入Activity的Context。这里我们处理通用场景,适配静态注册的Receiver:
2. 添加跳转到WiFi设置的逻辑
在弹窗的"确认"按钮点击事件里,创建一个跳转到系统WiFi设置界面的Intent,调用startActivity即可。需要注意的是,在Receiver中启动Activity必须添加FLAG_ACTIVITY_NEW_TASK标记。
修正后的完整BroadcastReceiver代码
public class ExampleBroadcastReceiver extends BroadcastReceiver { @Override public void onReceive(final Context context, Intent intent) { if (ConnectivityManager.CONNECTIVITY_ACTION.equals(intent.getAction())) { boolean noConnectivity = intent.getBooleanExtra( ConnectivityManager.EXTRA_NO_CONNECTIVITY, false ); if (noConnectivity) { // 用ContextThemeWrapper适配系统主题,避免样式异常 AlertDialog.Builder builder1 = new AlertDialog.Builder(new ContextThemeWrapper(context, android.R.style.Theme_DeviceDefault_Light_Dialog)); builder1.setMessage("您需要连接网络才能继续"); builder1.setCancelable(true); // 跳转WiFi设置的按钮逻辑 builder1.setPositiveButton( "去开启WiFi", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { // 创建跳转到WiFi设置的Intent Intent wifiSettingsIntent = new Intent(Settings.ACTION_WIFI_SETTINGS); // 必须添加NEW_TASK标记,Receiver中启动Activity的要求 wifiSettingsIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); context.startActivity(wifiSettingsIntent); dialog.dismiss(); } }); // 取消按钮逻辑 builder1.setNegativeButton( "稍后再说", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { dialog.cancel(); } }); AlertDialog dialog = builder1.create(); // 设置Window类型,避免BadTokenException dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY); dialog.show(); } else { Toast.makeText(context, "网络已连接", Toast.LENGTH_SHORT).show(); } } } }
重要提醒(Android 7.0+)
从Android N(API 24)开始,系统不再允许静态注册CONNECTIVITY_ACTION广播,静态注册的Receiver将收不到网络变化的通知。如果你的应用目标版本是API 24及以上,更推荐用官方方案:
- 动态注册BroadcastReceiver:在Activity的
onCreate里注册,onDestroy里注销。 - 或者使用ConnectivityManager.NetworkCallback(更稳定的替代方案),代码示例如下:
// 在Activity中初始化NetworkCallback private ConnectivityManager.NetworkCallback networkCallback; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); NetworkRequest networkRequest = new NetworkRequest.Builder() .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) .build(); networkCallback = new ConnectivityManager.NetworkCallback() { @Override public void onAvailable(Network network) { super.onAvailable(network); // 切换到主线程更新UI runOnUiThread(() -> Toast.makeText(MainActivity.this, "网络已连接", Toast.LENGTH_SHORT).show()); } @Override public void onLost(Network network) { super.onLost(network); runOnUiThread(() -> { // 直接用Activity的Context弹Dialog,无异常问题 AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this); builder.setMessage("您需要连接网络才能继续") .setPositiveButton("去开启WiFi", (dialog, which) -> { Intent intent = new Intent(Settings.ACTION_WIFI_SETTINGS); startActivity(intent); }) .setNegativeButton("稍后再说", (dialog, which) -> dialog.dismiss()) .show(); }); } }; // 注册网络回调 connectivityManager.registerNetworkCallback(networkRequest, networkCallback); } @Override protected void onDestroy() { super.onDestroy(); // 注销回调,避免内存泄漏 ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); connectivityManager.unregisterNetworkCallback(networkCallback); }
这样调整后,你的应用就能正常检测网络状态,弹出弹窗并引导用户开启WiFi啦!
内容的提问来源于stack exchange,提问作者Jdiavec C

