一、前言
在Android低版本使用存储功能的时候,仅需要在AndroidManifest.xml
中声明权限即可使用File
进行访问,然而从5.0开始存储权限去要用户进行动态授权。到了10.0开始,Android开始要求使用SAF文档框架进行存储功能的使用,11.0开始禁止用户访问Android/data/
目录,到了12.0开始,存储卡的最外层目录无法使用SAF框架获取。最近在开发过程中,需要同时访问多个根目录下面的文档,倘若使用SAF获取的话那么需要用户频繁授权。经过最终调研发现了只要用户授予以下权限,依然可以正常的使用File
类进行访问。并且在Android12上面依然可以和低版本一样使用。该方式可以实现文档管理器功能
二、AndroidManifest.xml
需要添加以下权限
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
<application
android:allowBackup="true"
android:preserveLegacyExternalStorage="true"
android:requestLegacyExternalStorage="true">
</application>
三、核心权限申请
@file:JvmName("PermissionUtils")
package com.rockey.clean.common.fileproviderutil
import android.Manifest
import android.app.AppOpsManager
import android.content.Intent
import android.content.pm.PackageManager
import android.net.Uri
import android.os.Build
import android.os.Environment
import android.provider.Settings
import androidx.annotation.RequiresApi
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.fragment.app.FragmentActivity
// AppOpsManager.OPSTR_MANAGE_EXTERNAL_STORAGE is a @SystemAPI at the moment
// We should remove the annotation for applications to avoid hardcoded value
const val MANAGE_EXTERNAL_STORAGE_PERMISSION = "android:manage_external_storage"
const val NOT_APPLICABLE = "N/A"
const val MANAGE_EXTERNAL_STORAGE_PERMISSION_REQUEST = 1
const val READ_EXTERNAL_STORAGE_PERMISSION_REQUEST = 2
fun getStoragePermissionName(): String {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
MANAGE_EXTERNAL_STORAGE_PERMISSION
} else {
Manifest.permission.READ_EXTERNAL_STORAGE
}
}
fun openPermissionSettings(activity: AppCompatActivity) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
requestStoragePermissionApi30(activity)
}
else {
activity.startActivity(
Intent(
Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
Uri.fromParts("package", activity.packageName, null)
)
)
}
}
fun getLegacyStorageStatus(): String {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
Environment.isExternalStorageLegacy().toString()
} else {
NOT_APPLICABLE
}
}
fun getPermissionStatus(activity: AppCompatActivity): String {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
checkStoragePermissionApi30(activity).toString()
} else {
checkStoragePermissionApi19(activity).toString()
}
}
fun checkStoragePermission(activity: FragmentActivity): Boolean {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
checkStoragePermissionApi30(activity)
} else {
checkStoragePermissionApi19(activity)
}
}
fun requestStoragePermission(activity: FragmentActivity) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
requestStoragePermissionApi30(activity)
}
// If you want to see the default storage behavior on Android Q once the permission is granted
// Set the "requestLegacyExternalStorage" flag in the AndroidManifest.xml file to false
else {
requestStoragePermissionApi19(activity)
}
}
@RequiresApi(30)
fun checkStoragePermissionApi30(activity: FragmentActivity): Boolean {
val appOps = activity.getSystemService(AppOpsManager::class.java)
val mode = appOps.unsafeCheckOpNoThrow(
MANAGE_EXTERNAL_STORAGE_PERMISSION,
activity.applicationInfo.uid,
activity.packageName
)
return mode == AppOpsManager.MODE_ALLOWED
}
@RequiresApi(30)
fun requestStoragePermissionApi30(activity: FragmentActivity) {
//val intent = Intent(Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION)
//以下方式可以跳转至指定的应用的设置页面,上面的Action无法做到
try {
val intent = Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION)
intent.data = Uri.parse("package:" + activity.packageName)//添加包名的方式使用这种方式
// intent.data = Uri.fromParts("package", activity.packageName, null); 这种方式也是可以的
activity.startActivityForResult(intent, MANAGE_EXTERNAL_STORAGE_PERMISSION_REQUEST)
} catch (e: Exception) {
try {
activity.startActivityForResult(Intent(Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION), MANAGE_EXTERNAL_STORAGE_PERMISSION_REQUEST)
} catch (e: Exception) {
}
}
//以下两种写法会出问题,需要注意
// val intent = Intent(Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION)
// intent.data = Uri.parse("package:" + activity.packageName)
// activity.startActivityForResult(intent, MANAGE_EXTERNAL_STORAGE_PERMISSION_REQUEST)
// 和
// val intent = Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION)
// activity.startActivityForResult(intent, MANAGE_EXTERNAL_STORAGE_PERMISSION_REQUEST)
}
@RequiresApi(19)
fun checkStoragePermissionApi19(activity: FragmentActivity): Boolean {
val status =
ContextCompat.checkSelfPermission(activity, Manifest.permission.READ_EXTERNAL_STORAGE)
return status == PackageManager.PERMISSION_GRANTED
}
@RequiresApi(19)
fun requestStoragePermissionApi19(activity: FragmentActivity) {
val permissions = arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE)
ActivityCompat.requestPermissions(
activity,
permissions,
READ_EXTERNAL_STORAGE_PERMISSION_REQUEST
)
}
四、使用方式
具体使用方式如下:
private fun initStorePermission(){
val isHasPermission = checkStoragePermission(requireActivity())
if (!isHasPermission){//假如没有权限则进行授权判断
requestStoragePermission(requireContext())
}else{
//已经授权成功
}
}
五、拓展资料
ACTION_GET_CONTENT和ACTION_OPEN_DOCUMENT之间的真正区别是什么?
android 调用系统文件管理器(打开手机自带的文件管理器)
ACTION OPEN DOCUMENT TREE only returns empty Recent folder