6、特殊权限:SYSTEM_ALERT_WINDOW、WRITE_SETTINGS、MANAGE_EXTERNAL_STORAGE
普通权限和危险权限,说白了就是「申请一下,用户点同意」就能搞定的事。但今天聊的这三个特殊权限,可就没那么温柔了。它们需要用户手动去系统设置里翻找,甚至要跳转专门的授权页面。嗯,我当年第一次处理 SYSTEM_ALERT_WINDOW 时,就被这个「手动授权」的流程坑过——用户明明点了同意,权限却没生效,排查了半天才发现是流程没走对。
6.1 为什么要有「特殊权限」?
你想想看,如果一个 App 能随意在其他应用上悬浮显示,或者能修改系统设置,甚至能访问所有文件——这得多危险?所以 Android 系统把这几个权限单独拎出来,用更严格的机制保护。
特殊权限的核心特点就三个:
- 不能直接申请:调用
requestPermissions()没用,系统会直接忽略 - 必须跳转设置:需要引导用户去「设置 → 特殊权限」里手动开启
- 状态可查询:可以用
Settings.canDrawOverlays()这类 API 检查是否已授权
核心原则:特殊权限的授权流程,本质上是「引导用户去系统设置里拨动开关」。你的代码只能做两件事——检查状态、跳转设置页。
6.2 SYSTEM_ALERT_WINDOW:悬浮窗权限
这个权限允许你的 App 在其他应用上方绘制内容。最常见的场景就是「悬浮球」「视频小窗」「聊天浮窗」。我个人习惯在需要显示悬浮窗之前,先检查权限状态,而不是一启动就弹窗。
6.2.1 权限声明
在 AndroidManifest.xml 中声明:
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
6.2.2 检查与申请
// 检查是否已授权
if (Settings.canDrawOverlays(this)) {
// 可以显示悬浮窗了
showFloatingWindow();
} else {
// 引导用户去设置页
Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
Uri.parse("package:" + getPackageName()));
startActivityForResult(intent, REQUEST_CODE_OVERLAY);
}
注意:ACTION_MANAGE_OVERLAY_PERMISSION 需要带上你的包名,否则系统会打开所有应用列表,用户还得手动找你的 App。我曾经见过一个 App 没传包名,用户找了半天没找到,直接差评走人。
6.2.3 处理授权结果
用户从设置页返回后,在 onActivityResult() 中重新检查权限:
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_CODE_OVERLAY) {
if (Settings.canDrawOverlays(this)) {
// 用户已授权
showFloatingWindow();
} else {
// 用户未授权,可以再次引导
Toast.makeText(this, "需要悬浮窗权限才能使用此功能", Toast.LENGTH_LONG).show();
}
}
}
6.3 WRITE_SETTINGS:修改系统设置权限
这个权限允许你的 App 修改系统设置,比如屏幕亮度、音量、自动旋转等。说白了,就是能碰 Settings.System 里的那些值。我记得有一次做阅读器 App,需要根据环境光自动调节亮度,就用了这个权限。
6.3.1 权限声明
<uses-permission android:name="android.permission.WRITE_SETTINGS" />
6.3.2 检查与申请
// 检查是否已授权
if (Settings.System.canWrite(this)) {
// 可以修改系统设置了
setScreenBrightness(128);
} else {
// 引导用户去设置页
Intent intent = new Intent(Settings.ACTION_MANAGE_WRITE_SETTINGS,
Uri.parse("package:" + getPackageName()));
startActivityForResult(intent, REQUEST_CODE_WRITE_SETTINGS);
}
小技巧:修改亮度时记得先保存原始值,用户退出功能后恢复。我曾经没做恢复逻辑,结果用户骂我「App 把我屏幕搞瞎了」。
6.3.3 实际修改示例
private void setScreenBrightness(int brightness) {
ContentResolver resolver = getContentResolver();
Settings.System.putInt(resolver, Settings.System.SCREEN_BRIGHTNESS, brightness);
}
6.4 MANAGE_EXTERNAL_STORAGE:管理所有文件权限
这个权限是 Android 11 引入的,用来替代旧的 READ_EXTERNAL_STORAGE 和 WRITE_EXTERNAL_STORAGE。它允许 App 访问设备上的所有文件,包括其他应用的文件。嗯,这个权限争议很大,Google 对它的审核非常严格。
6.4.1 权限声明
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
同时需要在 application 标签中添加:
<application
android:requestLegacyExternalStorage="true"
...>
6.4.2 检查与申请
// 检查是否已授权
if (Environment.isExternalStorageManager()) {
// 可以访问所有文件了
listAllFiles();
} else {
// 引导用户去设置页
Intent intent = new Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION,
Uri.parse("package:" + getPackageName()));
startActivityForResult(intent, REQUEST_CODE_MANAGE_STORAGE);
}
重要提醒:Google Play 对 MANAGE_EXTERNAL_STORAGE 有严格限制。如果你的 App 不是文件管理器、杀毒软件这类核心场景,基本不会通过审核。我有个朋友做了一款图片编辑 App,想用这个权限直接访问所有图片,结果被 Google 拒了三次。
6.5 三种特殊权限对比
| 权限 | 检查 API | 跳转 Action | 典型场景 |
|---|---|---|---|
| SYSTEM_ALERT_WINDOW | Settings.canDrawOverlays() | ACTION_MANAGE_OVERLAY_PERMISSION | 悬浮窗、视频小窗 |
| WRITE_SETTINGS | Settings.System.canWrite() | ACTION_MANAGE_WRITE_SETTINGS | 亮度调节、音量控制 |
| MANAGE_EXTERNAL_STORAGE | Environment.isExternalStorageManager() | ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION | 文件管理器、备份工具 |
6.6 特殊权限的通用处理流程
这三种权限虽然各自不同,但处理流程高度一致。我总结了一个通用模板,你可以直接套用:
// 1. 检查权限
if (checkSpecialPermission()) {
// 权限已授予,执行功能
doSomething();
} else {
// 2. 权限未授予,跳转设置页
Intent intent = new Intent(getSettingsAction());
intent.setData(Uri.parse("package:" + getPackageName()));
startActivityForResult(intent, REQUEST_CODE);
}
// 3. 在 onActivityResult 中重新检查
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_CODE) {
if (checkSpecialPermission()) {
doSomething();
} else {
// 用户拒绝,给出提示
showPermissionDeniedDialog();
}
}
}
避坑指南:我曾经遇到一个 Bug——用户从设置页返回后,onActivityResult() 没有被调用。原因是用户按了返回键,而不是直接关闭设置页。所以建议在 onResume() 中也做一次权限检查,双重保障。
6.7 知识体系图
下面这张图展示了特殊权限的完整知识结构,从分类到处理流程一目了然:
6.8 总结
特殊权限的处理,说白了就是三步走:检查、跳转、再检查。和危险权限最大的区别在于——你不能直接弹窗申请,必须把用户「请」到系统设置里去手动开关。
我个人建议你在代码里做好两件事:一是权限状态的实时监听(比如在 onResume 里检查),二是用户拒绝后的友好引导(别一直弹窗,给个明确的说明)。
嗯,特殊权限这块就聊到这儿。记住一点:权限越特殊,责任越大。滥用这些权限,轻则被用户卸载,重则被应用商店下架。
公众号:蓝海资料掘金营,微信deep3321