Android 11文件管理权限申请

Android 11文件管理权限申请,为什么需要这个权限,因为在Android 11后,无法直接在SDcard根目录写文件,Android 11之后要使用分区存储,但是分区存储使用起来很麻烦,所以可以申请文件管理权限,这样就可以随意读写SDcard了,写到根目录也没问题。

清单文件声明如下:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"package="cn.dazhou.permissionrequestdemo"xmlns:tools="http://schemas.android.com/tools"><uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/><uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"tools:ignore="ScopedStorage" /><applicationandroid:requestLegacyExternalStorage="true">。。。</application></manifest>

这里除了申请MANAGE_EXTERNAL_STORAGE权限之外,同时也是要申请WRITE_EXTERNAL_STORAGE权限的,因为在低于Android11的版本就需要使用WRITE_EXTERNAL_STORAGE权限,且需要在application节点添加android:requestLegacyExternalStorage="true",这个属性用于Android10版本可以免去分区存储。在Android11上,申请了MANAGE_EXTERNAL_STORAGE权限之后就不用申请WRITE_EXTERNAL_STORAGE权限了,因为拥有MANAGE_EXTERNAL_STORAGE权限应该就能任意读写SDCard了。根据实验,申请了MANAGE_EXTERNAL_STORAGE权限之后,再申请WRITE_EXTERNAL_STORAGE也是会弹出权限申请对话框的,所以,在Android11上还有没有必要申请WRITE_EXTERNAL_STORAGE权限,值得思考,但我就懒得去实验了。

class MainActivity : AppCompatActivity() {private lateinit var launcher: ActivityResultLauncher<Intent>override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_main)launcher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {Log.i("ABCD", "权限申请结果:${it.resultCode == Activity.RESULT_OK}")checkAndroid11FilePermission(this)}findViewById<Button>(R.id.button).setOnClickListener {checkAndroid11FilePermission(this)}}/** 检查Android 11或更高版本的文件权限 */private fun checkAndroid11FilePermission(activity: FragmentActivity) {// Android 11 (Api 30)或更高版本的写文件权限需要特殊申请,需要动态申请管理所有文件的权限if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {if (Environment.isExternalStorageManager()) {Log.i("ABCD","此手机是Android 11或更高的版本,且已获得访问所有文件权限")// TODO requestOtherPermissions() 申请其他的权限} else {Log.i("ABCD","此手机是Android 11或更高的版本,且没有访问所有文件权限")showDialog(activity, """本应用需要获取"访问所有文件"权限,请给予此权限,否则无法使用本应用""") {launcher.launch(Intent(Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION))}}} else {Log.i("ABCD","此手机版本小于Android 11,版本为:API ${Build.VERSION.SDK_INT},不需要申请文件管理权限")// TODO requestOtherPermissions() 申请其他的权限}}private fun showDialog(activity: FragmentActivity, message: String, okClick: () -> Unit) {AlertDialog.Builder(activity).setTitle("提示").setMessage(message).setPositiveButton("确定") { _, _ -> okClick() }.setCancelable(false).show()}}

注:在获取权限的结果时,不能使用it.resultCode == Activity.RESULT_OK来判断是否获得权限,因为这个结果永远为false。

Android 11和低版本的存储权限结合

为了兼容低于Android 11的版本,我们把之前的存储权限结合到一起,如下:

class MainActivity : AppCompatActivity() {private lateinit var storagePermissionLauncher: ActivityResultLauncher<String>private lateinit var android11StoragePermissionLauncher: ActivityResultLauncher<Intent>override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_main)if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {// Android 11或更高的版本android11StoragePermissionLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {requestStoragePermission(this)}} else {// Android 10或更低的版本storagePermissionLauncher = registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted ->if (isGranted) {Log.i("ABCD", "此手机是版本低于Android 11,且已获得存储权限")// TODO requestOtherPermissions() 申请其他的权限} else {Log.i("ABCD", "此手机是版本低于Android 11,且没有存储权限")val desc = if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) {// 权限被拒绝"""本应用需要获取"存储"权限,请给予此权限,否则无法使用本应用"""} else {// 权限被设置为不再提示"""本App需要使用"存储"权限,您需要到设置中打开此权限,否则无法使用本app"""}showDialog(this, desc) {storagePermissionLauncher.launch(Manifest.permission.WRITE_EXTERNAL_STORAGE)}}}}findViewById<Button>(R.id.button).setOnClickListener {requestStoragePermission(this)}}/** 请求存储权限 */private fun requestStoragePermission(activity: FragmentActivity) {Log.i("ABCD", "当前手机版本:API ${Build.VERSION.SDK_INT}")if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {// Android 11 (Api 30)或更高版本的写文件权限需要特殊申请,需要动态申请管理所有文件的权限if (Environment.isExternalStorageManager()) {Log.i("ABCD","此手机是Android 11或更高的版本,且已获得访问所有文件权限")// TODO requestOtherPermissions() 申请其他的权限} else {Log.i("ABCD","此手机是Android 11或更高的版本,且没有访问所有文件权限")showDialog(activity, """本应用需要获取"访问所有文件"权限,请给予此权限,否则无法使用本应用""") {android11StoragePermissionLauncher.launch(Intent(Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION))}}} else {// Android 10或更低的版本,申请存储权限Log.i("ABCD","此手机是版本低于Android 11,开始申请存储权限")storagePermissionLauncher.launch(Manifest.permission.WRITE_EXTERNAL_STORAGE)}}private fun showDialog(activity: FragmentActivity, message: String, okClick: () -> Unit) {AlertDialog.Builder(activity).setTitle("提示").setMessage(message).setPositiveButton("确定") { _, _ -> okClick() }.setCancelable(false).show()}}

工具类封装

object StoragePermissionUtil {private lateinit var storagePermissionLauncher: ActivityResultLauncher<String>private lateinit var android11StoragePermissionLauncher: ActivityResultLauncher<Intent>private lateinit var resultCallback: () -> Unit/*** 需求:* androidx.activity,1.2.0 或更高版本。* androidx.fragment,1.3.0 或更高版本。* 示例如下:* implementation "androidx.activity:activity-ktx:1.3.0"* implementation "androidx.fragment:fragment-ktx:1.3.6"*/fun registerForActivityResult(activity: FragmentActivity) {if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {// Android 11或更高的版本android11StoragePermissionLauncher = activity.registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {requestStoragePermission(activity, resultCallback)}} else {// Android 10或更低的版本storagePermissionLauncher = activity.registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted ->if (isGranted) {Log.i("ABCD", "此手机是版本低于Android 11,且已获得存储权限")resultCallback()} else {Log.i("ABCD", "此手机是版本低于Android 11,且没有存储权限")val desc = if (ActivityCompat.shouldShowRequestPermissionRationale(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE)) {// 权限被拒绝"""本应用需要获取"存储"权限,请给予此权限,否则无法使用本应用"""} else {// 权限被设置为不再提示"""本App需要使用"存储"权限,您需要到设置中打开此权限,否则无法使用本app"""}showDialog(activity, desc) {storagePermissionLauncher.launch(Manifest.permission.WRITE_EXTERNAL_STORAGE)}}}}}/** 请求存储权限 */fun requestStoragePermission(activity: FragmentActivity, resultCallback: () -> Unit) {this.resultCallback = resultCallbackLog.i("ABCD", "当前手机版本:API ${Build.VERSION.SDK_INT}")if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {// Android 11 (Api 30)或更高版本的写文件权限需要特殊申请,需要动态申请管理所有文件的权限if (Environment.isExternalStorageManager()) {Log.i("ABCD","此手机是Android 11或更高的版本,且已获得访问所有文件权限")resultCallback()} else {Log.i("ABCD","此手机是Android 11或更高的版本,且没有访问所有文件权限")showDialog(activity, """本应用需要获取"访问所有文件"权限,请给予此权限,否则无法使用本应用""") {android11StoragePermissionLauncher.launch(Intent(Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION))}}} else {// Android 10或更低的版本,申请存储权限Log.i("ABCD","此手机是版本低于Android 11,开始申请存储权限")storagePermissionLauncher.launch(Manifest.permission.WRITE_EXTERNAL_STORAGE)}}private fun showDialog(activity: FragmentActivity, message: String, okClick: () -> Unit) {AlertDialog.Builder(activity).setTitle("提示").setMessage(message).setPositiveButton("确定") { _, _ -> okClick() }.setCancelable(false).show()}}
class MainActivity : AppCompatActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_main)StoragePermissionUtil.registerForActivityResult(this)findViewById<Button>(R.id.button).setOnClickListener {StoragePermissionUtil.requestStoragePermission(this) {Log.i("ABCD", "拿到存储权限罗,开心^_^")}}}}

Android 11申请管理所有文件权限的Bug

在申请权限的时候,会弹出系统授权界面,当我们打开到如下界面后:

此时,我们把权限打开,然后返回到我们Activity是正常的。但是,如果在此界面,我们打开权限后,再关闭,此后不管你是要再开或者不开,当你返回Activity的时候,整个App会瞬间重启,而且它会自动创建Activity,而且奇怪的时候,它会自动把结果回调到新Activity中注册的监听器里,而在新Activity中,我们还没有点击按钮申请权限呢,所以StoragePermissionUtil.requestStoragePermission(this)代码就没有被执行到,则StoragePermissionUtil中的resultCallback变量就是未初始化的,所以授权结果就没办法返回到Activity中了,而且由于resultCallback没初始化就使用,就会抛出属性未初始化的异常。这属于系统Bug吧感觉,也没有什么好的解决方案了,在我的项目中,需要程序一运行就自动调用申请权限的函数,这种情况没有办法能很好的解决,比如可以把resultCallback在registerForActivityResult中进行传递,这样能保证不会出现属性未初始化的异常,但是按上面的操作,当出现App重启时,会收到两次回调结果,一次回调结果是重启前调用的申请回调到重启后的注册回调中,一次是重启后再一次申请权限所以也能获得一次回调。所以,这个Bug可以不管它罗,如果有用户非要这样操作,那就只能让App崩溃了,管不了那么多了。

Android11文件管理权限申请相关推荐

  1. Android11 读写权限申请

    Android11 读写权限申请 Android11系统对应用写入权限做了严格的限制.本文介绍如何获取文件读写权限.项目中 build.gradle 的targetSdkVersion >= 2 ...

  2. Android11及以上 文件读写权限申请

    Android11 读写权限申请 Android11系统对应用写入权限做了严格的限制.本文介绍如何获取文件读写权限.项目中 build.gradle 的targetSdkVersion >= 2 ...

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

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

  4. Android 动态权限申请过程

    Android 动态权限申请过程 --Android12 1.申请 申请前提,manifest一定要有,没有是不会通过的. 动态申请代码: public final void requestPermi ...

  5. Android权限申请完全解析(一):Android自带的权限申请

    1.为什么要权限申请 6.0以上就需要了,别问为什么.(不是重点,自行搜索) 2.如何进行权限申请 Android自带的权限申请 EasyPermission权限申请 Ps:EasyPermissio ...

  6. 如何正确处理Android6.0+的运行时权限申请

    从 Android 6.0(API 23) 开始,允许用户在应用运行时向其授予权限,好处有: 简化应用安装过程,无需在安装或更新时授予权限 用户可以对应用的功能进行更多控制:例如,用户可以选择为相机应 ...

  7. API 23之前的版本都是自动获取权限,而从 Android 6.0 开始添加了权限申请的需求,更加安全。

    这里以单个存储权限为例: · 在 Manifest 中添加访问权限:(只需设置可写,因为可写必定可读) <uses-permission android:name="android.p ...

  8. Android 6.0动态权限申请

    Android 6.0动态权限申请 特别感谢: https://www.jianshu.com/p/2fe4fb3e8ce0 本文是在该文的基础上添加了类似自己的读后感才完成的. 我并不希望成为白求恩 ...

  9. 【Android 应用开发】动态权限管理示例 ( 使用原生代码实现 | 申请权限 | 判定权限申请结果 | 判定 “ 不再询问 “ 情况 )

    文章目录 一.申请权限 二.判定权限申请结果 三.判定 " 不再询问 " 情况 四.完整代码示例 1.权限管理代码 2.主界面代码 3.执行结果 五.博客资源 一.申请权限 首先 ...

最新文章

  1. 计算机中的机器码就是二进制数,高电单片机习题(答案)
  2. 微信支付教程系列之公众号支付
  3. 给JBoss种蛊分析
  4. Eureka Client的使用
  5. 关于JavaFX的最常见问题
  6. Linux shell 脚本SDK 打包实践, 收集assets和apk, 上传FTP
  7. 逻辑右移和算术右移区别
  8. 罕有数据库系统之比拟 - Oracle数据库
  9. 使用 CodeIgniter 框架快速开发 PHP 应用(四)
  10. 【转】ACE开发环境搭建
  11. 拼多多商品详情接口、拼多多商品基本信息、拼多多商品属性接口
  12. 区块链开发以太坊ETH单位转换关系
  13. 普罗米修斯 软件_利用普罗米修斯软件制作课件
  14. 速来围观!看小伙是如何用python可视化各城市拥堵情况的
  15. ios开发 服务器通信协议,iOS开发网络篇—HTTP协议
  16. D - 一只小蜜蜂...
  17. SSD(ECCV 2016)
  18. h5与原生app交互的原理
  19. Hive秒数转成时分秒
  20. 多线程是啥?有啥用?(上)

热门文章

  1. 产品经理从入门到精通视频教程全集
  2. Java(Servlet)模糊查询
  3. PMP计算题公式知识点整理
  4. 会考access数据库操作题_信息技术学业水平考试操作题必备!!!
  5. android代码设置maxlength,android-使用maxLength使用3个点结束TextView
  6. 微信小程序:云开发·初探
  7. esp8266-01s加人体热释红外探头做的手机远程报警器
  8. C语言指针 动画展示 指针到底是什么 指针的本质 C语音深入剖析
  9. Timeunit与sleep
  10. java time sleep_TimeUnit类中的sleep() 和Thread.sleep()