安卓11改变了此前安卓系统对于文件管理的规则,在安卓11上,文件读写变成了特殊权限。应用默认只能读写自己的目录/android/data/包名
Android11系统对应用写入权限做了严格的限制。本文介绍如何获取文件读写权限。
项目中 build.gradle 的targetSdkVersion >= 29 ,会出现读写问题
为了能直接usb安装,gradle.properties 需要设置(否则,在安装时会报异常:-15)
android.injected.testOnly=false
申请权限,主要用到如下4个函数
int checkSelfPermission(String)
void requestPermissions(int, String...)
boolean shouldShowRequestPermissionRationale(String)
void onRequestPermissionsResult(int,String[],int[])
上述四个方法中,前三个方法在support-v4的ActivityCompat中都有,建议使用兼容库中的方法。最后一个方法是用户授权或者拒绝某个权限组时系统会回调Activity或者Fragment中的方法。
checkSelfPermission
的返回值有如下两种
PackageManager.PERMISSION_DENIED
PackageManager.PERMISSION_GRANTED
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;}}
}
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);
}
跳转到应用信息页面,页面包含如下内容
需要手动点击“权限”按钮,进入“应用权限”页面,然后开始设置相应的数据
谷歌官方的安卓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
切记,不要在安卓11以下调用该Intent,会导致闪退
安卓11及以后,有如下几个函数判断是否赋予存储权限
ActivityCompat.checkSelfPermission(context, Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED
ActivityCompat.checkSelfPermission(context, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED
ActivityCompat.checkSelfPermission(context, Manifest.permission.MANAGE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED
Environment.isExternalStorageManager()
isExternalStorageManager => false
READ_EXTERNAL_STORAGE => false
READ_EXTERNAL_STORAGE => false
MANAGE_EXTERNAL_STORAGE => false
isExternalStorageManager => true
READ_EXTERNAL_STORAGE => true
WRITE_EXTERNAL_STORAGE => true
MANAGE_EXTERNAL_STORAGE => false
isExternalStorageManager => false
READ_EXTERNAL_STORAGE => true
WRITE_EXTERNAL_STORAGE => true
MANAGE_EXTERNAL_STORAGE => false
isExternalStorageManager => true
READ_EXTERNAL_STORAGE => false
WRITE_EXTERNAL_STORAGE => false
MANAGE_EXTERNAL_STORAGE => false
因此,可以得出如下结论
MANAGE_EXTERNAL_STORAGE
权限,因为其永远为falseisExternalStorageManager
、ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION
、ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION
API,只能在安卓11及以上版本调用(低版本调用会闪退)isExternalStorageManager
变为true;但反之在“所有文件访问权限”页面授予权限,并不会改变WRITE_EXTERNAL_STORAGE
的允许情况因此,为了兼容 安卓4.4-安卓12,在11以下,建议使用checkSelfPermission
方法进行存储权限判断;而安卓11及以上使用Environment.isExternalStorageManager
即可