在适配Android8.0+之前,应用内使用系统级的弹框:
//manifest
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
//java
IconTextDialog iconTextDialog = new IconTextDialog(context, context.getResources().getString(R.string.iscontinue), R.drawable.ic_popup_icon_error, leaveDialogListener);
iconTextDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);//重点
show(iconTextDialog);
运行在Android8.1设备上报错:
System.err: Caused by: android.view.WindowManager$BadTokenException: Unable to add window android.view.ViewRootImpl$W@cd9c6dc -- permission denied for window type 2003
System.err: at android.view.ViewRootImpl.setView(ViewRootImpl.java:789)
System.err: at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:356)
System.err: at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:93)
System.err: at android.app.Dialog.show(Dialog.java:330)
怎么处理?
这是因为在Android8.0+的系统中,Google规定申请"android.permission.SYSTEM_ALERT_WINDOW"权限的应用,需要给悬浮窗口设置Type: WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY.如下:
IconTextDialog iconTextDialog = new IconTextDialog(context.getApplicationContext(), context.getResources().getString(R.string.iscontinue), R.drawable.ic_popup_icon_error, leaveDialogListener);
//iconTextDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);//这里换掉了
iconTextDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY);//这里换掉了
show(iconTextDialog);
但,申请"android.permission.SYSTEM_ALERT_WINDOW"权限,需要使用如下方式:
private final int REQUEST_SYSTEM_ALERT_WINDOW = 10068;//自定义
Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);
intent.setData(Uri.parse("package:" + getPackageName()));
startActivityForResult(intent, REQUEST_SYSTEM_ALERT_WINDOW);
之后在,onActivityResut中获取权限授权结果:
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch (requestCode) {
case REQUEST_SYSTEM_ALERT_WINDOW:
//TODO 验证权限是否申请成功,并进行下一步
break;
}
}
还有其他情况么?
- 引用了错误的context : 在一个普通的应用内Dialog中,使用了applicationContext,换个activity的context吧。
- 发生在内存泄漏时 :context溢出了,所以在Activity销毁前,要正确关闭弹窗才行。
- PopupWindow在Activity的onCreate()中就调用了show,需要等待Activity控件渲染完毕再调用。