做一个应用 Log 的实时显示,发现需要用到
SYSTEM_ALERT_WINDOW权限,视同通用方式获取后发现并不行。
# 普通权限获取流程
我们在项目中采用的是 PermissionsDispatcher, 使用方式也很简单,只需要在类上注解 @RuntimePermissions , 写一个 public void 的方法,例如 initAfterPermissionChecked , 此方法中写权限获取后的操作即可,方法上用 @NeedsPermission(Manifest.permission.CAMERA) 注解即可,多个权限可以写成下面的形式
@NeedsPermission({Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION}) |
编译代码后自动生成 类名 + PermissionsDispatcher 的 Java 文件,例如 LaunchActivityPermissionsDispatcher , 在需要获取权限的地方直接调用 方法名 + WithPermissionCheck(this); 生成的 Java 文件中已经给你生成好了这个方法,例如 initAfterPermissionCheckedWithPermissionCheck() , 其他针对各种授权与否的处理就不细说了。
# 问题
对于 SYSTEM_ALERT_WINDOW 权限,我本想采用相同的方法处理,直接在权限列表后加入 Manifest.permission.SYSTEM_ALERT_WINDOW ,结果编译后直接报错
Method 'initAfterPermissionChecked()' defines 'android.permission.SYSTEM_ALERT_WINDOW' with other permissions at the same time. |
根据这个 issues

大概意思就是 WRITE_SETTINGS 和 SYSTEM_ALERT_WINDOW 这类权限不能作为普通权限申请流程的一部分一起申请,因为我们一般需要重新跳转到设置界面去授予权限。
# 解决
在新的类中获取 SYSTEM_ALERT_WINDOW 权限,同样的流程,类名上注解 @RuntimePermissions ,新建 public void 方法并注解 @NeedsPermission(Manifest.permission.SYSTEM_ALERT_WINDOW) ,这里我写的是
@NeedsPermission(Manifest.permission.SYSTEM_ALERT_WINDOW) | |
public void checkSystemAlertPermission(){ | |
} |
获取权限到地方直接用的
LoginActivityPermissionsDispatcher.checkSystemAlertPermissionWithPermissionCheck(this); |
获取。编译运行后发现存在一个问题,那就是这个权限会直接跳转到设置界面,我们需要给个 Dialog 让用户选择
new AlertDialog.Builder(this) | |
.setTitle("提示") | |
.setMessage("显示Log需要在设置中打开系统悬浮窗权限,是否打开?") | |
.setPositiveButton("确定", (dialog, which) -> { | |
LoginActivityPermissionsDispatcher.checkSystemAlertPermissionWithPermissionCheck(this); | |
dialog.dismiss(); | |
}) | |
.setNegativeButton("取消", (dialog, which) -> { | |
dialog.dismiss(); | |
}) | |
.show(); |
我们同样不能再进入的时候每次都弹窗让用户选择,毕竟该权限可能已经授予了。于是我选择了使用 ContextCompat 或者 PermissionChecker 的 checkSelfPermission() 方法来校验当前权限是否获取到,但是每次获取的值都是 PackageManager.PERMISSION_DENIED , 于是我们采用另一种方法去获取权限是否授予
Settings.canDrawOverlays(this) |
方法注释如下

至此,我们就能正常获取 SYSTEM_ALERT_WINDOW 权限了
if (!Settings.canDrawOverlays(this)) { | |
new AlertDialog.Builder(this) | |
.setTitle("提示") | |
.setMessage("显示Log需要在设置中打开系统悬浮窗权限,是否打开?") | |
.setPositiveButton("确定", (dialog, which) -> { | |
LoginActivityPermissionsDispatcher.checkSystemAlertPermissionWithPermissionCheck(this); | |
dialog.dismiss(); | |
}) | |
.setNegativeButton("取消", (dialog, which) -> { | |
dialog.dismiss(); | |
}) | |
.show(); | |
} |