Android存储分区

Android存储分区 知识点记录


Android存储

  1. 内部存储
    /data/data/com.xxx.xxx/
    一般用于存储容量较小的,私密性较强的文件
    App卸载后被删除,会被统计进存储

  2. 外部存储
    1)外部私有空间:
    /sdcard/Android/data/com.xxx.xxx/
    一般用于存储容量较大的文件,即使删除了也不影响App正常功能
    App卸载后被删除,会被统计进存储
    2)外部共享空间(所有app共享):
    媒体文件:
    /sdcard/Movies/
    /sdcard/DCIM/(相册)
    文档和其他文件:
    /sdcard/Documents/

在Android 10 之前 ,用户可以随意在SD 卡创建文件目录,比如我们能够直接在/sdcard/目录下创建目录/文件,好处是不进入统计,且方便操作文件,缺点是不安全

Google在Android 10.0上引入Scoped Storage分区存储,原理如下:

  1. App访问自身内部空间与 访问外部私有目录不需要任何权限(与Android 10.0之前一致)
  2. 外部共享空间、外部其它目录,App无法通过路径直接访问,不能新建、删除、修改目录/文件等, 需要通过Uri访问

以下目录无需存储权限即可访问:

  1. App自身的内部存储
  2. App自身的自带外部存储-私有目录

剩下的都需要申请存储权限,Android 10.0前后对于存储作用域访问的区别就体现在如何访问剩余这些目录内的文件。


Android6.0之前

无需申请动态权限的,在AndroidManifest.xml 里声明存储权限即可:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
这样即可访问外部共享和外部其他


Android6.0-Android9.0

需要动态申请权限,除了在AndroidManifest.xml 里声明存储权限外,还需要在代码里动态申请,可以使用第三方库

外部共享

访问媒体文件(这里以图片为例)

  1. 直接构造路径获取
  2. 通过MediaStore获取路径
  3. 通过MediaStore获取Uri,图片的信息封装在Uri里,通过Uri构造出InputStream,再进行图片解码拿到Bitmap

访问文档和其它文件

  1. 直接构造路径获取
  2. SAF(这里以图片为例):Storage Access Framework 简称SAF:存储访问框架。相当于系统内置了文件选择器,通过它可以拿到想要访问的文件信息
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
private void startSAF() {
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
//选择图片
intent.setType("image/jpeg");
startActivityForResult(intent, 100);
}

@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);

if (requestCode == 100) {
//选中返回的图片封装在uri里
Uri uri = data.getData();
openUri(uri);
}
}

private void openUri(Uri uri) {
try {
//从uri构造输入流
InputStream fis = getContentResolver().openInputStream(uri);
Bitmap bitmap = BitmapFactory.decodeStream(fis);
} catch (Exception e) {

}
}
外部其他目录
  1. 直接构造路径获取
  2. SAF 与上述一致
上述总结:
  1. 通过路径访问。路径可以直接构造也可以通过MediaStore获取。
  2. 通过Uri访问。Uri可以通过MediaStore或者SAF获取。

Android10及以后(开始有分区)

外部共享,外部其他,无法通过路径访问,不能创建/删除/修改,可以通过Uri访问

targetSdkVersion<=28或者 android:requestLegacyExternalStorage="true" 可禁用分区存储

上述是缓冲方法,不能一劳永逸,且Android11会强制开启分区

外部共享空间

系统里有external.db数据库,该数据库里有files表,该表里存放着共享文件的诸多信息,如图片有宽高,经纬度、存放路径等,视频宽高、时长、存放路径等。而文件真正存放的地方在于共享存储空间。

  1. 保存图片到相册
    路径写入数据库,并获取Uri,Uri构造输出流,将该图片保存在/sdcard/Pictures/目录下
  2. 从相册获取图片
    从Cursor中获取Uri,Uri构造输入流读取图片

视频音频同理

外部其他

txt文件读写,Uri不能通过MediaStore获取,只能通过SAF获取

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
private void startSAF() {
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
//指定选择文本类型的文件
intent.setType("text/plain");
startActivityForResult(intent, 100);
}

@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);

if (requestCode == 100) {
//选中返回的文件信息封装在Uri里
Uri uri = data.getData();

openUriForRead(uri);
//openUriForWrite(uri);

}
}
外部私有(不需要权限)

创建文件:
File externalFilesDir = getExternalFilesDir(null);
getExternalFilesDir: /storage/emulated/0/Android/data/com.stew.androidtest/files


MediaStore作用

其内部有Audio、Images等内部类,这些内部类里记录着files表的各个字段名,通过构造这些参数就可以插入相应的字段值以及获取对应的字段值。


总结

  • 内部或者外部私有
    自己的文件存放自己目录:
    /sdcard/Android/data/packagename/ 和/data/data/packagename/
  • 外部共享(媒体,文件)
    共享存储空间里的文件需要通过Uri构造输入输出流访问,Uri获取方式有两种:MediaStore和SAF。
  • 外部其他
    暂时不建议操作

参考文章:
https://juejin.cn/post/7012108220982362149
https://juejin.cn/post/7012259637734948895
https://juejin.cn/post/7012262477828194340