Android 11及以上授予文件管理权限

news/2024/11/9 5:04:29/

背景

安卓11改变了此前安卓系统对于文件管理的规则,在安卓11上,文件读写变成了特殊权限。应用默认只能读写自己的目录/android/data/包名

gradle配置

Android11系统对应用写入权限做了严格的限制。本文介绍如何获取文件读写权限。

项目中 build.gradle 的targetSdkVersion >= 29 ,会出现读写问题

  • 当targetSdkVersion = 29,通过设置requestLegacyExternalStorage=“true”,还能解决
  • 当targetSdkVersion = 30后,需要申请所有文件权限才能获取到写入权限

为了能直接usb安装,gradle.properties 需要设置(否则,在安装时会报异常:-15)

android.injected.testOnly=false

AndroidManifest添加权限设置

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<!-- Android11额外添加 -->
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"tools:ignore="ScopedStorage" /><application...android:requestLegacyExternalStorage="true"...>

语法说明

申请权限,主要用到如下4个函数

  1. 检查权限
int checkSelfPermission(String)
  1. 申请权限
void requestPermissions(int, String...)
  1. 是否应该显示请求权限的说明
boolean shouldShowRequestPermissionRationale(String)
  1. 处理权限结果回调
void onRequestPermissionsResult(int,String[],int[])

上述四个方法中,前三个方法在support-v4的ActivityCompat中都有,建议使用兼容库中的方法。最后一个方法是用户授权或者拒绝某个权限组时系统会回调Activity或者Fragment中的方法。

checkSelfPermission的返回值有如下两种

  1. 已拒绝 PackageManager.PERMISSION_DENIED
  2. 已授权 PackageManager.PERMISSION_GRANTED

动态权限申请

Android 6.0之上Android11以下申请权限

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {// 6.0String[] perms = {Manifest.permission.READ_EXTERNAL_STORAGE,Manifest.permission.WRITE_EXTERNAL_STORAGE,Manifest.permission.READ_PHONE_STATE};for (String p : perms) {int ret = ContextCompat.checkSelfPermission(activity, p);if (ret != PackageManager.PERMISSION_GRANTED) {//TODO 跳转到权限页,请求权限return;}}
}

Android11申请权限

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {//判断是否有管理外部存储的权限if (!Environment.isExternalStorageManager()) {//TODO 跳转到权限页,请求权限}
}

跳转系统授权页面

跳转到“应用信息”页面

安卓默认只能跳转到 "应用信息"页面,但是国内手机厂商大多支持各自自定义的Intent,直接跳到应用程序权限页面

/*** 当前应用详情页面(在该页面单击权限,进入的是权限组页面)*/
public static void goAppDetailsSettings(Context context) {Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);intent.setData(Uri.fromParts("package", context.getPackageName(), null));context.startActivity(intent);
}

在这里插入图片描述

跳转到应用信息页面,页面包含如下内容

  1. 存储
  2. 电量
  3. 流量
  4. 通知
  5. 权限
  6. 默认打开

需要手动点击“权限”按钮,进入“应用权限”页面,然后开始设置相应的数据

在这里插入图片描述
在这里插入图片描述

跳转到“所有文件访问权限”

谷歌官方的安卓11在“应用权限”页面隐藏了对“存储”权限的设置(当然,很多国产机,即使到了安卓12,依旧支持原先的存储权限;但也存在部分不支持的,比如:OPPO Reno9 Pro+)

取而代之的是“所有文件访问权限”页面,该页面用来授予“所有文件的管理权限”,允许此应用读取、修改和删除此设备或任意已连接存储卷上的所有文件。如果您授予该权限,应用无需明确通知您即可访问文件

/*** 进入Android 11或更高版本的文件访问权限页面*/
private void goManagerFileAccess(AppCompatActivity activity) {// Android 11 (Api 30)或更高版本的写文件权限需要特殊申请,需要动态申请管理所有文件的权限if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {Intent appIntent = new Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION);appIntent.setData(Uri.parse("package:" + getPackageName()));//appIntent.setData(Uri.fromParts("package", activity.getPackageName(), null));try {activity.startActivity(appIntent);} catch (ActivityNotFoundException ex) {ex.printStackTrace();Intent allFileIntent = new Intent(Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION);activity.startActivity(allFileIntent);}}
}

这里面涉及到两个Settings

  1. 当前应用的文件访问权限:ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION
  2. 所有需要文件访问权限的应用:ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION

切记,不要在安卓11以下调用该Intent,会导致闪退

在这里插入图片描述

测试存储权限是否授权

安卓11及以后,有如下几个函数判断是否赋予存储权限

  1. 老版读权限
ActivityCompat.checkSelfPermission(context, Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED
  1. 老版写权限
ActivityCompat.checkSelfPermission(context, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED
  1. 安卓11新出的管理外置存储权限
ActivityCompat.checkSelfPermission(context, Manifest.permission.MANAGE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED
  1. 安卓11新出的判断是否是外置存储管理器
Environment.isExternalStorageManager()

具体实验(VIVO Y77,安卓12)

  1. 初始化状态
isExternalStorageManager => false
READ_EXTERNAL_STORAGE   => false
READ_EXTERNAL_STORAGE   => false
MANAGE_EXTERNAL_STORAGE  => false
  1. 仅在“应用权限”页面,打开存储权限中的“允许管理所有文件”
isExternalStorageManager => true
READ_EXTERNAL_STORAGE   => true
WRITE_EXTERNAL_STORAGE   => true
MANAGE_EXTERNAL_STORAGE  => false
  1. 仅在“应用权限”页面,打开存储权限中的“仅允许访问媒体文件”
isExternalStorageManager => false
READ_EXTERNAL_STORAGE   => true
WRITE_EXTERNAL_STORAGE   => true
MANAGE_EXTERNAL_STORAGE  => false
  1. 仅在“所有文件访问权限”页面,授予权限
isExternalStorageManager => true
READ_EXTERNAL_STORAGE   => false
WRITE_EXTERNAL_STORAGE   => false
MANAGE_EXTERNAL_STORAGE  => false

因此,可以得出如下结论

  1. 不要尝试判断 MANAGE_EXTERNAL_STORAGE 权限,因为其永远为false
  2. isExternalStorageManagerACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSIONACTION_MANAGE_ALL_FILES_ACCESS_PERMISSIONAPI,只能在安卓11及以上版本调用(低版本调用会闪退)
  3. “应用权限”页面,打开存储权限中的“允许管理所有文件”会使得isExternalStorageManager变为true;但反之在“所有文件访问权限”页面授予权限,并不会改变WRITE_EXTERNAL_STORAGE的允许情况

因此,为了兼容 安卓4.4-安卓12,在11以下,建议使用checkSelfPermission方法进行存储权限判断;而安卓11及以上使用Environment.isExternalStorageManager即可


http://www.ppmy.cn/news/2026.html

相关文章

【元胞自动机】模拟电波在整个心脏中的传导和传播的时空动力学研究(Matlab代码实现)

&#x1f468;‍&#x1f393;个人主页&#xff1a;研学社的博客 &#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜…

《WEB安全漏洞100讲》(第4讲)CSRF漏洞

1.CSRF漏洞原理 CSRF(Cross-site request forgery)&#xff0c;跨站请求伪造&#xff0c;简写 CSRF/XSRF。指利用受害者尚未失效的身份认证信息&#xff08;cookie、会话等&#xff09;&#xff0c;诱骗其点击恶意链接或者访问包含攻击代码的页面&#xff0c;在受害人不知情的…

面试10分钟就完事了,问的实在是太...

干了两年外包&#xff0c;本来想出来正儿八经找个互联网公司上班&#xff0c;没想到算法死在另一家厂子。 自从加入这家外包公司&#xff0c;每天都在加班&#xff0c;钱倒是给的不少&#xff0c;所以也就忍了。没想到11月一纸通知&#xff0c;所有人不许加班&#xff0c;薪资…

线段树模板(Java)

线段树一、线段树概念二、线段树模板1.建树2. 单点修改3.区间查询4.完整代码及测试一、线段树概念 线段树是一种二叉搜索树&#xff0c;与区间树相似&#xff0c;它将一个区间划分成一些单元区间&#xff0c;每个单元区间对应线段树中的一个叶结点。它的主要优势是对于区间求和…

从入门到精通,收下这 22 个 Python 学习网站

今天一并给大家整理推送&#xff0c;希望能帮你在这条道路上&#xff0c;走得更顺畅&#xff0c;走得更远&#xff0c;更稳… 0. 学习整体思路 我做为一个过来人&#xff0c;有一些经验想要分享&#xff1a; 前期&#xff1a;花点时间选一门口碑上佳的入门电子文字教程&…

SM4分组密码算法

对称加密算法SM4SM4算法介绍一、SM4加密流程二、轮函数F1.合成置换T3.非线性变换τ2.线性变换L4.加密的结果总结SM4算法介绍 SM4.0于2013年3月被列为国家密码行业标准“GM/T 0002-2012《SM4分组密码算法》&#xff08;原SMS4分组密码算法&#xff09;”。2016年被列入国家标准…

【Map 和 WeakMap 的区别】

Map 1.传统对象结构 Map本质上是一个键值对的集合。和传统对象结构相比&#xff0c;传统对象只能用字符串作为键名&#xff0c;这在使用上造成了很大的限制。 const data {} //element为节点对象 const element document.querySelector(.node) console.log(element) //输…

探花交友_第2章_环境搭建(新版)

探花交友_第2章_环境搭建&#xff08;新版&#xff09; 文章目录探花交友_第2章_环境搭建&#xff08;新版&#xff09;课程介绍 《探花交友》1、项目介绍1.1、项目背景1.2、市场分析1.3、目标用户群体1.4、使用场景1.5、竞争对手分析1.5.1、竞品选择1.5.2、竞品分析1.6、项目简…