android 权限申请的一点理解(理论)

读写sd卡的权限时序图

读写sd卡的权限时序图理解

  1. 初始化不触发回调initSqliteRWPermissionLauncher() 仅注册申请器,全程不调用 onSqliteRWPermissionResult
  2. 回调触发条件 :只有「调用 checkAndRequestSqliteRWPermission() 发起申请 + 用户操作权限弹窗」后,才会回调 onSqliteRWPermissionResult
  3. allGranted 来源 :由系统返回的 result 解析生成(读 / 写权限都授权才为 true),不是初始化时获取;
  4. goToAppSetting 作用:仅作为「授权失败」后的兜底引导,需用户确认后再调用,不替代权限申请流程。

权限申请代码

复制代码
/**
     * 初始化:仅适配SQLite读写的SD卡权限申请器(重命名后)
     * 仅初始化权限申请器,注册结果回调通道
     */
    private void initSqliteRWPermissionLauncher() {
        // 核心修改:sdCardPermissionLauncher 替代原 permissionLauncher
        sdCardPermissionLauncher = registerForActivityResult(
                new ActivityResultContracts.RequestMultiplePermissions(),
                result -> {
                    // 检查读写权限是否都授权
                    boolean readGranted = result.getOrDefault(Manifest.permission.READ_EXTERNAL_STORAGE, false);
                    boolean writeGranted = result.getOrDefault(Manifest.permission.WRITE_EXTERNAL_STORAGE, false);
                    onSqliteRWPermissionResult(readGranted && writeGranted);
                }
        );
    }
    /**
     * SQLite读写权限结果回调(子类重写)
     * @param allGranted 读写权限是否都授权
     */
    protected void onSqliteRWPermissionResult(boolean allGranted) {}

    /**
     * 检查并申请SD卡公共目录的SQLite读写权限(重命名后)
     * 私有目录无需调用此方法!
     * 1. 检查权限是否已授权;
     * 2. 未授权则根据 Android 版本发起权限申请(Android 13 + 弹提示弹窗,低版本弹系统权限弹窗);
     * 3. 授权结果通过 onSqliteRWPermissionResult() 回调
     *
     * 发起权限申请的核心入口:包含检查
     */
    protected void checkAndRequestSqliteRWPermission() {
        // 仅申请读写外部存储权限(SQLite是通用文件,无需媒体权限)
        String[] permissions = new String[]{
                Manifest.permission.READ_EXTERNAL_STORAGE,
                Manifest.permission.WRITE_EXTERNAL_STORAGE
        };

        // 检查权限是否已授权
        boolean readGranted = ContextCompat.checkSelfPermission(mContext, Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED;
        boolean writeGranted = ContextCompat.checkSelfPermission(mContext, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED;
        if (readGranted && writeGranted) {
            onSqliteRWPermissionResult(true);
            return;
        }

        // Android 13+ 特殊处理:读写通用文件需引导到设置页
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
            new MaterialAlertDialogBuilder(mContext)
                    .setTitle("权限申请")
                    .setMessage("需要读写SD卡中的SQLite数据库,请手动开启存储权限(跳转至设置页)")
                    .setPositiveButton("前往设置", (dialog, which) -> {
                        // 核心修改:调用重命名后的 sdCardPermissionLauncher
                        sdCardPermissionLauncher.launch(permissions);
                    })
                    .setNegativeButton("取消", (dialog, which) -> onSqliteRWPermissionResult(false))
                    .show();
        } else {
            // Android 12及以下:直接弹权限弹窗(调用重命名后的变量)
            sdCardPermissionLauncher.launch(permissions);
        }
    }

    /**
     * 工具方法:检查SQLite读写权限是否已授权
     * 遍历权限列表,调用 ContextCompat.checkSelfPermission 判断是否授权
     */
    protected boolean isSqliteRWPermissionGranted() {
        boolean readGranted = ContextCompat.checkSelfPermission(mContext, Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED;
        boolean writeGranted = ContextCompat.checkSelfPermission(mContext, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED;
        return readGranted && writeGranted;
    }

    /**
     * 可选:引导到应用设置页(权限拒绝时调用)
     * 仅跳转到应用的系统设置页(用户需手动在设置页开启存储权限),无权限检查、无结果回调
     */
    protected void goToAppSetting() {
        android.content.Intent intent = new android.content.Intent();
        intent.setAction(android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
        android.net.Uri uri = android.net.Uri.fromParts("package", mContext.getPackageName(), null);
        intent.setData(uri);
        startActivity(intent);
    }

详细的过程理解

初始化二行代码

复制代码
 boolean readGranted = result.getOrDefault(Manifest.permission.READ_EXTERNAL_STORAGE, false);
                boolean writeGranted = result.getOrDefault(Manifest.permission.WRITE_EXTERNAL_STORAGE, false);

这两行代码是权限申请结果的解析逻辑,核心作用是从权限申请的返回结果中,精准获取「读取外部存储」和「写入外部存储」两个权限的授权状态,下面拆解每一部分的含义:

一、先理解核心 API:result 的本质

resultRequestMultiplePermissions(多权限申请)契约的返回值,类型为 Map<String, Boolean>

  • Key :权限常量(如 Manifest.permission.READ_EXTERNAL_STORAGE);
  • Valuetrue = 用户授权,false = 用户拒绝。

二、逐行解析代码

1. boolean readGranted = result.getOrDefault(Manifest.permission.READ_EXTERNAL_STORAGE, false);
部分 含义
result.getOrDefault(...) Map 的安全取值方法:① 先根据 Key(权限常量)查找对应的 Value(授权状态);② 若 Key 不存在(比如申请时漏传该权限),则返回默认值 false
Manifest.permission.READ_EXTERNAL_STORAGE 要查询的权限 Key:「读取外部存储」权限;
false 默认值:若未查询到该权限的授权结果,默认视为 "未授权";
整行作用 获取「读取外部存储」权限的授权状态,无结果时默认未授权,赋值给 readGranted
2. boolean writeGranted = result.getOrDefault(Manifest.permission.WRITE_EXTERNAL_STORAGE, false);

逻辑和上一行完全一致,只是查询的权限 Key 换成了「写入外部存储」(Manifest.permission.WRITE_EXTERNAL_STORAGE),最终获取该权限的授权状态并赋值给 writeGranted

三、为什么要用 getOrDefault 而不是直接 get

直接用 result.get(权限Key) 存在空指针风险!比如:若代码中不小心漏申请了某个权限,result 中就没有该 Key,result.get() 会返回 null,赋值给 boolean 类型变量时会抛出 NullPointerException

getOrDefault 是 "安全取值":即使 Key 不存在,也会返回默认值 false,避免崩溃。

四、这两行代码的实际作用

结合后续逻辑 onSqliteRWPermissionResult(readGranted && writeGranted),这两行的最终目的是:只有「读取」和「写入」权限都被授权(两个变量都为 true),才判定为 "权限全部授权",否则判定为 "未授权"

五、示例对比(安全 vs 不安全)

写法 安全性 场景测试 结果
result.getOrDefault(权限Key, false) 安全 漏申请「写入」权限 返回 false,最终判定未授权,无崩溃
result.get(权限Key) 不安全 漏申请「写入」权限 返回 null,赋值给 boolean 时崩溃

总结

这两行代码的核心是:安全解析多权限申请的返回结果,分别获取「读」「写」两个权限的授权状态,为后续判断 "是否全部授权" 提供依据,它是 "权限申请后的结果解析",而非 "提前检查授权状态"。

权限初始化及执行过程

  1. initSqliteRWPermissionLauncher() → 仅初始化权限申请器 (注册 "申请权限→接收结果" 的通道),不获取任何权限值
  2. 当调用 checkAndRequestSqliteRWPermission() 发起权限申请后,用户操作(允许 / 拒绝)→ 系统返回权限结果 → 申请器解析出读写权限的 value → 回调给 onSqliteRWPermissionResult
  3. onSqliteRWPermissionResult 中判断未授权 → 不能直接调用 goToAppSetting(),需先弹窗提示用户,再让用户选择是否跳设置页(避免强制跳转);
  4. 用户点击 "读写数据库" 按钮(click 事件)→ 先调用 checkAndRequestSqliteRWPermission()(检查权限,未授权则发起申请,已授权则直接读写)。

完整逻辑链路

  1. 页面创建 → 调用 initSqliteRWPermissionLauncher() → 准备好"权限申请工具"(无任何弹窗);

  2. 用户点击"读写数据库"按钮 → 调用 checkAndRequestSqliteRWPermission();

  3. checkAndRequestSqliteRWPermission() 先检查权限: - 已授权 → 直接回调 onSqliteRWPermissionResult(true) → 执行读写数据库; - 未授权 → 发起权限申请(Android 13+弹提示弹窗,低版本弹系统权限弹窗);

  4. 用户操作权限弹窗(允许/拒绝)→ 工具解析结果(readGranted/writeGranted)→ 回调 onSqliteRWPermissionResult;

  5. onSqliteRWPermissionResult 处理结果: - 授权成功 → 执行读写数据库; - 授权失败 → 弹窗提示用户 → 用户选择"前往设置"→ 调用 goToAppSetting() 跳设置页;

  6. 用户在设置页开启权限后返回 → onResume 中重新检查权限 → 已授权则自动执行读写(可选)。

精确理解初始化里面的**onSqliteRWPermissionResultallGranted**

步骤 对应代码 通俗解释 allGranted 状态
1. 初始化 initSqliteRWPermissionLauncher() 你去快递公司,领了空白快递单(注册申请器),但没填单、没寄件 allGranted,甚至这个参数还没诞生
2. 发起申请 checkAndRequestSqliteRWPermission() 你填好快递单(指定要申请的读写权限),交给快递员(调用 launch() 仍无 allGranted,等待收件人(用户)回复
3. 用户操作 系统弹权限弹窗,用户点「允许 / 拒绝」 收件人(用户)签收 / 拒收快递,快递员把结果写在回执上 回执上才有「是否全部签收」(allGranted)的结果
4. 回调 onSqliteRWPermissionResult(allGranted) 快递员把回执交给你,你拿到 allGranted 的值 allGranted 被赋值(true/false),进入回调逻辑

allGranted 的生成逻辑(代码层面)

allGranted 不是 "初始化时获取",而是权限申请有结果后,通过以下两步生成

  1. 用户操作权限弹窗后,系统返回 resultMap<String, Boolean>,键是权限常量,值是授权状态);

  2. 代码解析 result,计算出 allGranted

    java

    运行

    复制代码
    // 这是初始化时注册的回调逻辑,但只有发起申请后才会执行
    result -> {
        boolean readGranted = result.getOrDefault(Manifest.permission.READ_EXTERNAL_STORAGE, false);
        boolean writeGranted = result.getOrDefault(Manifest.permission.WRITE_EXTERNAL_STORAGE, false);
        // 只有发起申请并得到结果,这行才会执行,allGranted 才会被赋值
        onSqliteRWPermissionResult(readGranted && writeGranted);
    }

三、关键结论:allGranted 的两个核心特点

  1. 时序上allGranted 只有在「发起权限申请 + 用户操作」后才会产生,初始化阶段完全触及不到;
  2. 来源上allGranted 来自「本次申请的用户操作结果」,不是「系统全局权限状态」(全局状态靠 isSqliteRWPermissionGranted() 检查);
  3. 触发上onSqliteRWPermissionResult 是被动回调 ------ 你不用手动调用这个方法,只有发起申请并得到结果后,系统才会自动调用它,并把 allGranted 传进去。

四、反例验证:初始化时永远拿不到 allGranted

如果只执行初始化,不发起申请:

复制代码
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    initSqliteRWPermissionLauncher(); // 仅初始化,不发起申请
    // 此时 onSqliteRWPermissionResult 永远不会被调用,allGranted 不存在
}

只有调用 checkAndRequestSqliteRWPermission() 发起申请,且用户操作后,onSqliteRWPermissionResult 才会被触发,allGranted 才有值。

五、补充:容易混淆的 "提前检查" vs "回调结果"

你可能把「初始化前的权限检查」和「回调的 allGranted」搞混了:

  • 提前检查(isSqliteRWPermissionGranted()):是查「系统里是否已经授权」,结果不会触发 onSqliteRWPermissionResult
  • 回调的 allGranted:是查「本次申请用户是否授权」,只有发起申请后才会有。

比如:

  • 用户之前已经授权过读写权限,你调用 checkAndRequestSqliteRWPermission() 时,会直接触发 onSqliteRWPermissionResult(true)(无需弹弹窗),但这依然是「发起申请后」的回调,不是「初始化时」的结果。

最终总结

onSqliteRWPermissionResultallGranted 参数:

✅ 是「发起权限申请 + 用户操作」后才有的结果;

❌ 绝对不是初始化时获取的;

❌ 也不是主动去查的,而是系统自动回调给你的。

初始化只是 "准备工具",只有发起申请,工具才有 "干活的机会",才会产生 allGranted 并触发回调。

相关推荐
Digitally1 小时前
如何将联系人从电脑传输到POCO手机?4种方法
android
一只小羊啊1 小时前
Vue + Android WebView 实现大文件 PDF 预览完整解决方案
android·vue.js·pdf·webview
a31582380612 小时前
Android Framework开发知识点整理
android·java·linux·服务器·framework·android源码开发
k***825113 小时前
图文详述:MySQL的下载、安装、配置、使用
android·mysql·adb
小七有话说13 小时前
DevUI与企业级中后台系统融合:低代码表单构建器实战
android·rxjava·devui
暗碳14 小时前
安卓abx二进制xml文件转换普通xml文件
android·xml
4z3315 小时前
Android15 Framework(3):系统服务进程 SystemServer 解析
android·源码阅读
没有了遇见15 小时前
Android 之Google Play bundletool 校验 AAB包
android·google