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

news2024/11/22 13:41:32

背景

安卓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.0
	String[] 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.coloradmin.cn/o/66877.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

YOLOv5 模型结构及代码详细讲解(一)

王旭*&#xff0c;沈啸彬 *, 张钊* (淮北师范大学计算机科学与技术学院&#xff0c;淮北师范大学经济与管理学院&#xff0c;安徽 淮北) *These authors contributed to the work equllly and should be regarded as co-first authors. &#x1f31e;欢迎来到深度学习的世界 …

window本地编译Spring源码并运行

1. Gradle 下载地址 https://services.gradle.org/distributions/ 2. Jdk下载地址&#xff1a; https://www.oracle.com/java/technologies/downloads/#java11-windows 3. Spring源码下载地址 https://github.com/spring-projects/spring-framework/tags 4. 注意事项 以…

微服务守护神-Sentinel-流控规则

引言 书接上篇 微服务守护神-Sentinel-概念 &#xff0c;上面介绍了Sentinel相关概念&#xff0c;本篇就来看下Sentinel的流控规则。 流控规则 流量控制&#xff0c;其原理是监控应用流量的QPS(每秒查询率) 或并发线程数等指标&#xff0c;当达到指定的阈值时 对流量进行控…

OSG开发-使用VisualStudio2019创建CMake项目方式开发HelloOSG

本文保证你使用VS2019&#xff0c;可以把这个OSG程序开发出来&#xff0c;看到那个蓝色的带有纹理的地球。 大概步骤如下&#xff1a; 下载已经编译好的OSG的库和头文件新建一个CMake项目编辑make.txt编写main.cpp运行。下载已经编译好的OSG的库和头文件 由于我们需要用OSG的…

blender 常用修改器

文章目录前置.阵列.倒角.布尔.精简.拆边.镜像.螺旋.实体化表面细分.三角化.体积到网格.焊接修改器.蒙皮.线框.铸型.曲线修改器.置换修改器.晶格修改器.缩裹修改器.简易修改.表面形变修改 .多级精度修改器.前置. 注意&#xff0c;修改器未应用前&#xff0c;只能操作原物体 阵…

自动驾驶:2022 apollo day 观后感(三)

自动驾驶&#xff1a;2022 apollo day 观后感&#xff08;三&#xff09;TOPIC THREE&#xff1a; 文心大模型在自动驾驶感知中的落地应用&#xff08;王井东&#xff09;多传感器融合autolabeling的发展&#xff1a;大模型数据闭环大模型&#xff0c;已经成为自动驾驶能力提升…

差动驱动机器人轨迹-CoCube

轨迹博客&#xff1a; 玫瑰线轨迹如何规划&#xff1f;&#xff08;desmosROS2turtlesim……&#xff09; ROS1云课→23turtlesim绘制小结&#xff08;数学和编程&#xff09; 如上所涉及的机器人假定模型都是差动驱动机器人。 许多移动机器人使用一种称为差动驱动的驱动机构…

vue项目图片裁剪上传——vue-cropper的使用,裁剪后上传头像

vue项目图片裁剪上传——vue-cropper的使用&#xff0c;裁剪后上传头像 npm地址&#xff1a;https://www.npmjs.com/package/vue-cropper github地址&#xff1a;https://github.com/xyxiao001/vue-cropper 在线demo&#xff1a;http://github.xyxiao.cn/vue-cropper/exampl…

学习分享:如何进行全局变量的学习

​对于很多朋友&#xff0c;尤其是刚接触全局变量的朋友而言&#xff0c;全局变量的学习对他们来说不是一件容易的事情。关于这方面的学习&#xff0c;很多朋友不太理解它的用法及分析方法&#xff0c;所以会比较乱&#xff0c;难以掌握。 什么是axure全局变量&#xff1f;全局…

【STM32】详解PWM的概念和原理

PWM的概念和原理一、PWM是什么&#xff1f;二、如何实现&#xff1f;三、STM32中的PWM四、使用库函数配置PWM将LED0设置为呼吸灯一、PWM是什么&#xff1f; PWM&#xff08;Pulse width modulation&#xff09;脉冲宽度调制。PWM是通过编程控制输出方波的频率和占空比&#xf…

oracle自启动的p***并行进程过多导致的process进程超限问题

某项目现场反馈无任何业务连接&#xff0c;查询v$process仍有500多个进程&#xff1b; 查询会话连接&#xff0c;也只有十几个会话&#xff1b; select b.MACHINE, b.PROGRAM,b.USERNAME, count(*) from v$process a, v$session b where a.ADDR b.PADDR and b.USERNAME is…

RCNN学习笔记-ResNeXt

论文地址&#xff1a;https://arxiv.org/pdf/1611.05431.pdf Abstract 我们提出了一种简单、高度模块化的图像分类网络体系结构。我们的网络是通过重复一个构建块来构建的&#xff0c;该构建块聚合了一组具有相同拓扑的变换。我们的简单设计产生了一个只有几个超参数可设置的…

JavaScript -- 数组常用方法及示例代码总结

文章目录数组的方法1 Array.isArray()2 at()3 concat()4 indexOf()5 lastIndexOf()6 join()7 slice()8 push()9 pop()10 unshift()11 shift()12 splice()13 reverse()14 sort()15 forEach()16 filter()17 map()18 reduce()数组的方法 Array参考文档&#xff1a;https://develo…

AIoT通用组件服务攻略之设备“收纳”好帮手——分组管理

天翼物联网平台&#xff08;AIoT&#xff09;通用组件服务提供设备分组管理功能&#xff0c;既可将单个产品下的部分设备划成一个分组&#xff0c;也可将多个产品下的设备划成一个统一分组&#xff0c;主要用于对设备进行归类管理&#xff0c;便于对设备进行批量操作。 对设备…

Java中高级核心知识全面解析——类加载过程

一个非数组类的加载阶段&#xff08;加载阶段获取类的二进制字节流的动作&#xff09;是可控性最强的阶段&#xff0c;这一步我们可以去完成还可以自定义类加载器去控制字节流的获取方式&#xff08;重写一个类加载器的 loadClass() 方法&#xff09;。数组类型不通过类加载器创…

适用于嵌入式单片机的差分升级通用库+详细教程

1. 什么是差分/增量升级&#xff1f; 借用网上的介绍&#xff1a;适合嵌入式的差分升级又叫增量升级&#xff0c;顾名思义就是通过差分算法将源版本与目标版本之间差异的部分提取出来制作成差分包&#xff0c;然后在设备通过还原算法将差异部分在源版本上进行还原从而升级成目…

Jekyll如何自定义摘要

最近搭建博客网站的时候遇到一个问题&#xff1a;博客的摘要包含了内容的格式&#xff0c;比如下面这张图。 标题的样式显示在摘要中&#xff0c;这可太奇怪了。我在查找文档之后没有想到合适的解决方案&#xff0c;于是乎就去 Jekyll 的项目下面提了个 Issue 问了一下。 在…

js原生实现步骤条

实现思路: 1.定義一個流程數組和一个步骤状态 2.遍历这个流程数组&#xff0c;如果步骤状态大于流程&#xff0c;checked&#xff1d;true&#xff0c; 3.页面输出遍历的流程数组&#xff0c;checked的div点亮 最终效果 <!DOCTYPE html> <html> <title>js原…

音视频开发:直播推流技术指南

一、推流架构 推流SDK客户端的模块主要有三个&#xff0c;推流采集端、队列控制模块、推流端。其中每个模块的主要流程如下&#xff0c;本文的主要目的就是拆分推流流程。 1.1 采集端 视频采集&#xff1a;通过Camera采集视频。 音频采集&#xff1a;通过麦克风采集音频。 …

HTML做一个节日页面【六一儿童节】纯HTML代码

&#x1f389;精彩专栏推荐 &#x1f4ad;文末获取联系 ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业&#xff1a; 【&#x1f4da;毕设项目精品实战案例 (10…