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 的 数据流持续发射 + 冷/热切换 + 生命周期感知 完美覆盖权限变更监听的三个需求:

  1. 持续发射:通过 flow { … } 包装权限检查逻辑,每次变更时发射新状态。
  2. 热流共享:使用 StateFlow 或 SharedFlow 让多个收集者共享最新的权限状态。
  3. 生命周期安全:配合 .flowWithLifecycle() 只在 UI 可见时收集,避免后台泄漏。
  4. 无需弹窗:通过监听 PermissionManager.OnPermissionChangeCallback(API 30+)或定时轮询,无需触发系统 UI。

对比总结:Activity Result API 适合 一次性授权请求;Flow 方案适合 持续权限状态观测。实际应用中可将两者组合:用 Activity Result 发起首次请求,使用 Flow 持续监听后续变化。

1.4 权限变更监听流程对比图

权限变更监听三种方案对比流程 权限变更监听方案对比 运行时权限模型 • Android 6.0+ 动态申请 • 仅在弹窗时回调 • Settings 关闭无通知 • 使用 checkSelfPermission • 可配“不再询问” 仅响应授权弹窗 生命周期短暂 ⚠ 局限性 1. 无法感知后台撤销 2. 状态需手动同步 3. 不支持增量变化 4. 需 Activity 上下文 Activity Result API • registerForActivityResult • 类型安全,回调单向 • 一次性响应弹窗 • 绑定生命周期 • 需 onStart 前注册 仅处理弹窗结果 无后续监听能力 ⚠ 局限性 1. 无法监听 Settings 2. 状态无法共享 3. 不支持后台服务 4. 每次需 launch Flow 方案 • 持续发射状态 • StateFlow 共享 • 可监听 API 30+ • 生命周期安全 • 支持后台收集 实时感知任何变更 无弹窗依赖 ✅ 优势 1. 持续发射 2. 热流共享 3. 安全收集 4. 可组合

1.5 何时选择 Flow 方案

  • 应用需要实时展示当前权限状态(如相机权限开关指示灯)。
  • 应用在 后台或前台 Service 中也需感知权限撤销(如录音权限被关闭后自动暂停录制)。
  • 多个组件(ViewModel、Fragment、Service)共享同一权限状态。
  • 需要在权限状态变化时触发非弹窗逻辑(如日志记录、UI 刷新、权限重检)。

注意:Flow 方案需要在 Android 11+ 上使用 PermissionManager.addPermissionChangeCallback 实现低功耗监听;在更低版本上需结合轮询(建议使用 intervalFlow 配合 delay 避免高频检测)。

1.6 下章预览方向