1. 认识权限变更监听场景
Android 运行时权限模型自 6.0 (API 23) 引入后,权限授予状态不再是一次性静态配置,而是随用户操作动态变化。传统方案依赖 onRequestPermissionsResult 回调或 Activity Result API 处理授权结果,但两者都无法持续监听权限状态的后续变更 —— 例如用户在系统 Settings 中手动关闭权限、或系统因权限策略撤销授权。本章从原理出发,对比三种方案的优缺点,并揭示 Kotlin Flow 在权限变更监听场景下的结构性优势。
1.1 运行时权限模型核心原理
运行时权限将权限分为 Normal 与 Dangerous 两类。Normal 权限自动授予;Dangerous 权限需要应用弹出系统对话框请求用户授权,并且用户可在 Settings 中随时撤销。权限状态通过 PackageManager.checkSelfPermission() 查询,返回 PERMISSION_GRANTED 或 PERMISSION_DENIED。
| 概念 | 说明 |
|---|---|
| 授权粒前沿 | 每个 Dangerous 权限独立弹窗,用户可拒绝或“不再询问” |
| 状态持久性 | 授权状态存储在 /data/system/users/0/runtime-permissions.xml |
| 变更触发 | Settings 撤销、权限策略更新、AppOps 策略调整 |
| 监听手段 | 无系统广播;需通过 OnPermissionChangeCallback(Android 11+)或轮询 |
关键事实:系统仅在 授权弹窗返回 时通知应用,之后用户去 Settings 关闭权限,应用进程默认收不到任何回调。这是传统方案的核心缺陷。
1.2 Activity Result API 的局限
AndroidX Activity Result API 封装了 registerForActivityResult 配合 RequestPermission(),解决了 onRequestPermissionsResult 的耦合问题,但仍存在以下硬伤:
- 一次性响应:API 只在弹窗返回时触发回调,无法感知后续 Settings 中的权限关闭。
- 生命周期绑定:必须在 Activity/Fragment 的
onStart前注册,不适合 Service 或后台场景。 - 状态穿透困难:如需在多个 Fragment 或 ViewModel 间同步权限状态,需要手动传递或依赖 LiveData。
- 无法增量响应:如果用户反复开关权限,每次都需要重新调用
launch()触发弹窗,无法后台静默跟踪。
// Activity Result API 示例 —— 仅能处理一次弹窗结果
private val requestPermissionLauncher =
registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted ->
// isGranted 只在弹窗结束时得到,后续 Settings 变更不会进入此回调
if (isGranted) {
// 处理授权
}
}
// 发起授权请求
requestPermissionLauncher.launch(Manifest.permission.CAMERA)
1.3 Flow 方案的优势
Kotlin Flow 的 数据流持续发射 + 冷/热切换 + 生命周期感知 完美覆盖权限变更监听的三个需求:
- 持续发射:通过
flow { … }包装权限检查逻辑,每次变更时发射新状态。 - 热流共享:使用
StateFlow或SharedFlow让多个收集者共享最新的权限状态。 - 生命周期安全:配合
.flowWithLifecycle()只在 UI 可见时收集,避免后台泄漏。 - 无需弹窗:通过监听
PermissionManager.OnPermissionChangeCallback(API 30+)或定时轮询,无需触发系统 UI。
对比总结:Activity Result API 适合 一次性授权请求;Flow 方案适合 持续权限状态观测。实际应用中可将两者组合:用 Activity Result 发起首次请求,使用 Flow 持续监听后续变化。
1.4 权限变更监听流程对比图
1.5 何时选择 Flow 方案
- 应用需要实时展示当前权限状态(如相机权限开关指示灯)。
- 应用在 后台或前台 Service 中也需感知权限撤销(如录音权限被关闭后自动暂停录制)。
- 多个组件(ViewModel、Fragment、Service)共享同一权限状态。
- 需要在权限状态变化时触发非弹窗逻辑(如日志记录、UI 刷新、权限重检)。
注意:Flow 方案需要在 Android 11+ 上使用 PermissionManager.addPermissionChangeCallback 实现低功耗监听;在更低版本上需结合轮询(建议使用 intervalFlow 配合 delay 避免高频检测)。