注意: 本篇文章讲述的是外部存储。
首先,“Android 官方文档中”外部存储就是目前我们手机上能看到的【手机存储空间】。
很久之前,手机内存不足,是需要插入SD卡的,所以是外部存储空间。由于历史遗留原因 后面就没有改回。
下图是文章总结:
1. 了解Android 存储分类:
这里要搞清楚,SD卡只是外部存储的一种。网址
2.了解 应用目录 和 共享目录的 概念。
Android 会在存储卡的 "Android/data" 目录下给每个应用单独建立一个文件目录(称为应用目录,只有应用自己可以访问,此目录会随着应用卸载删除)。
共享目录:所有应用均可访问的空间,/storage/emulated/0/下的公共文件夹。
/* 获取当前应用目录 下的 Download路径 */ String path = getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS).toString(); /* 获取当前APP 缓存目录 */ getExternalCacheDir() /* 获取公共共享目录 下的 Download路径 */ String path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).toString();3. Android 外部存储访问权限变化过程:
Android 4.4
Android 4.4 之前 随意访问外部存储。
从Android 4.4 开始,只能访问自身应用目录,如需访问外部存储其它空间,需要在 AndroidManifest.xml 中声明申请如下权限。
<!-- 访问外部存储中应用目录之外的任何文件, Android 13 废弃 --> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/> <!-- 向应用目录以外的任何文件写入数据, Android 13 废弃 --> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>Android 6.0
从Android 6.0 开始,系统将部分权限归类为“危险权限”(如摄像头、定位、存储等),这些危险权限需要请求权限,步骤如下:
1. 检查 APP 是否开启了指定权限
ContextCompat.checkSelfPermission(Context, permission):判断指定权限是否开启。
/* 1. 定义String 数组, 存储要申请的权限 */ private String[] permissions_10 = new String[]{ Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE }; private void request() { /* android 6.0 开始 自己请求运行时权限 */ /* 1.2 定义 List 列表,存储未授权的权限 */ List<String> permissionList = new ArrayList<>(); if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.Q) { /* Android 10和之前 只需要申请如下两项权限, Android 13.+ 这两项权限废弃 */ for (String permisssion: permissions_10) { if (ContextCompat.checkSelfPermission(this, permisssion) != PackageManager.PERMISSION_GRANTED) { permissionList.add(permisssion); } } }2. 请求系统弹窗,供用户选择是否开启权限
ActivityCompat.requestPermissions(Activity, String[] permissions, int requestCode):申请指定的权限
/* 2. 申请 未授权 的权限 */ if (!permissionList.isEmpty()) { ActivityCompat.requestPermissions(this, permissionList.toArray(new String[0]), 17); }3. 判断用户的权限选择结果
重写 onRequestPermissionsResult() 方法,判断结果
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); // requestCode不能为负数,也不能大于2的16次方即65536 if (requestCode == 17) { boolean allGranted = true; for (int result: grantResults) { if (result != PackageManager.PERMISSION_GRANTED) { // 判断权限是否授权成功 allGranted = false; break; } } if (allGranted) { // 权限全部授权成功 } else { Toast.makeText(this, "部分功能被拒绝,功能可能无法正常使用", Toast.LENGTH_SHORT).show(); } } }Android 7.0
不能使用 真实路径file://,需要使用content://URI(路径对象)。
将字符串格式的文件路径转为 Uri 对象:Uri.parse(String file_path)。
Android 10
Android 10.0 开始:Android 外部存储 开始实行作用域存储(分区存储),即分为只有应用自己可以访问的私有应用目录和 所有应用均可访问的共享公共目录。
默认可访问自身应用目录和自身应用创建的媒体文件(使用MediaStore方法)。
访问外部存储的其它文件(除其他私有应用目录):
最简单方式——暂时停用作用域存储并声明+请求之前那两个权限,在 AndoridManifest.xml 的 application 节点声明如下:
<manifest ... > <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/> <!-- 向应用专属目录以外的任何文件写入数据, Android 13 废弃 --> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <!-- 暂时停用分区存储, Android 11 失效 --> <application android:requestLegacyExternalStorage="true" ...> </application> </manifest>Android 11
与Android 10 还可以停用分区存储不同,Android 11 开始,强制执行分区存储机制。
分区存储机制:应用只能访问自己的应用目录,无法访问属于其他应用的应用专属目录。
访问权限不变,写入权限改变。
访问除应用专属目录和 MediaStore 之外文件的写入权限:在 AndroidManifest.xml 声明如下权限并请求该权限。
<!-- Android 11 引入, 拥有除应用专属目录和 MediaStore 之外文件的写入权限 --> <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/>if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { /* Android 11.+ 需申请 MANAGE_EXTERNAL_STORAGE */ if (!Environment.isExternalStorageManager()) { /** 判断当前应用是否拥有 所有文件访问权限 **/ // 注意:特殊权限 需要通过 Intent 引导用户前往设置页开启 !!! Intent intent = new Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION); intent.setData(Uri.parse("package:"+getPackageName())); startActivity(intent); } }Android 13
Android 13 开始 访问权限不变,媒体权限细化 导致 申请权限改变。Android 媒体权限细化
之前的那两个权限废弃,新的申请权限如下:
如果你们发现有什么问题,可以评论区提出。感谢!