Android 6.0.1设备AlertDialog崩溃求助,需适配全Android版本
这个崩溃的核心原因很明确:你在BroadcastReceiver的onReceive方法中尝试直接显示AlertDialog,但这里使用的context是Receiver的上下文,不具备有效的应用窗口令牌(window token)。Android 6.0.1对非应用上下文创建窗口的限制比Android 8.0更严格,所以才会触发Unable to add window -- token null is not for an application错误。
正确的解决方案:用透明Activity承载Dialog
在Receiver中直接弹Dialog是不符合Android规范的(Receiver生命周期极短,且没有窗口环境),最稳妥的兼容方案是启动一个透明的Activity,在这个Activity里显示你的AlertDialog。这样既能保证有有效的窗口令牌,又能在所有Android版本上正常运行。
步骤1:创建透明主题的Activity
首先在AndroidManifest.xml中注册一个透明的Activity:
<activity android:name=".DialogActivity" android:theme="@style/TransparentActivityTheme" android:launchMode="singleInstance" android:excludeFromRecents="true" /> <!-- 可选:不让Activity出现在最近任务列表 -->
然后在res/values/styles.xml中定义透明主题:
<style name="TransparentActivityTheme" parent="Theme.AppCompat.Light.NoActionBar"> <item name="android:windowBackground">@android:color/transparent</item> <item name="android:windowIsTranslucent">true</item> <item name="android:windowAnimationStyle">@android:style/Animation.Translucent</item> <item name="android:windowNoTitle">true</item> </style>
步骤2:在透明Activity中实现AlertDialog
编写DialogActivity.java,把原来的Dialog逻辑移到这里:
public class DialogActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); AlertDialog.Builder builder = new AlertDialog.Builder(this, R.style.MyAlertDialogStyle); builder.setMessage("Would you like to enable it?") .setTitle("No Internet Connection") .setPositiveButton(" Enable Internet ", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { Intent dialogIntent = new Intent(Settings.ACTION_SETTINGS); startActivity(dialogIntent); finish(); // 关闭透明Activity } }) .setNegativeButton("Cancel", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { dialog.dismiss(); finish(); // 关闭透明Activity } }); AlertDialog alert = builder.create(); alert.setCancelable(false); alert.show(); } }
步骤3:修改BroadcastReceiver的逻辑
在NetworkChangeReceiver的onReceive方法中,不再直接显示Dialog,而是启动这个透明Activity:
@Override public void onReceive(Context context, Intent intent) { // 这里保留你的网络状态判断逻辑... Intent dialogActivityIntent = new Intent(context, DialogActivity.class); dialogActivityIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); // 从Receiver启动Activity必须加这个flag context.startActivity(dialogActivityIntent); }
为什么Android 8.0能正常运行?
Android 8.0对BroadcastReceiver的行为做了一些调整,同时对非应用上下文创建窗口的限制有所放宽,所以你的代码在8.0上偶然能运行,但这并不是规范的实现方式,在低版本系统中必然会出现崩溃。
额外注意事项
- 不要尝试用
Application Context加TYPE_SYSTEM_ALERT的方式显示Dialog:这种方式需要SYSTEM_ALERT_WINDOW权限,而且在Android 6.0+中需要用户手动授权悬浮窗权限,体验很差,不推荐。 - 透明Activity的
finish()一定要调用:确保用户操作Dialog后,透明Activity能及时销毁,避免占用资源。
内容的提问来源于stack exchange,提问作者Kavin-K

