这几天把《第一行代码》里用广播实现强制下线功能的程序实现了一下,发现最后在实现强制下线功能,弹出警告窗口时,会导致程序奔溃。查看日志,发现错误
java.lang.RuntimeException: Unable to start receiver test.zyf.com.broadcasttest.ForceOfflineReceiver: android.view.WindowManager$BadTokenException: Unable to add window android.view.ViewRootImpl$W@7d4996d -- permission denied for this window type
没有获得警告窗口权限,可是明明已经声明权限android.permission.SYSTEM_ALERT_WINDOW
。最后,折腾了好久,终于发现问题出在6.0版本,权限SYSTEM_ALERT_WINDOW
属于特殊权限,在申明权限之后还必须发送请求获得用户授权。这是6.0版本的动态权限管理机制,意在提高用户体验和程序安全性。
现在提供一种解决方法,在原先代码的基础上,首先新建一个PermissionUtils类
public class PermissionUtils {
private static final String TAG = "PermissionUtils";
public static final int PERMISSION_SETTING_REQ_CODE = 0x1000;
/**
* 检测系统弹出权限
*/
@TargetApi(23)
public static boolean checkSettingAlertPermission(Object cxt, int req) {
if (cxt instanceof Activity) {
Activity activity = (Activity) cxt;
if (!Settings.canDrawOverlays(activity.getBaseContext())) {
Log.i(TAG, "Setting not permission");
Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
Uri.parse("package:" + activity.getPackageName()));
activity.startActivityForResult(intent, req);
return false;
}
} else if (cxt instanceof Fragment) {
Fragment fragment = (Fragment) cxt;
if (!Settings.canDrawOverlays(fragment.getActivity())) {
Log.i(TAG, "Setting not permission");
Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
Uri.parse("package:" + fragment.getActivity().getPackageName()));
fragment.startActivityForResult(intent, req);
return false;
}
} else {
throw new RuntimeException("cxt is net a activity or fragment");
}
return true;
}
}
接下来为强制下线按钮设置监听事件
forceOffline.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View v){
if(PermissionUtils.checkSettingAlertPermission(MainActivity.this, PermissionUtils.PERMISSION_SETTING_REQ_CODE == true)){
Intent intent = new Intent("test.zyf.com.broadcasttest.FORCE_OFFLINE");
sendBroadcast(intent);
}
}
});
重写方法onActivityResult()
或着onRequestPermissionsResult()
方法,这里我自己用第一种方法实现
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == PermissionUtils.PERMISSION_SETTING_REQ_CODE) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (Settings.canDrawOverlays(this)) {
return;
} else {
Toast.makeText(this, "not has setting permission", Toast.LENGTH_LONG).show();
finish();
}
}
}
}
折腾好几天,记录一下。
PS:6.0版本前的几个版本均可以正常运行《第一行代码》上的程序