关于 Android 的文件存储目录
众所周知,在 Android 中,文件的存储有多个路径可供存储,也提供了多个 Api 使用,那这些 Api 到底是用来是哪个目录,又有什么区别呢。
内部存储和外部存储
首先,要先知道 Android 存储中分为内部存储(Internal storage)和外部存储(External storage)
下面用 com.application.id 作为我们的 applicationId 来举例
内部存储
- 内部存储指的是 App 私有的目录,即 /data/data/com.application.id/
有些手机的目录是 /data/user/0/com.application.id/
实际上是同一个目录,从下图可见,/data/user/0
目录是一个软连接,其实际指向的目录即/data/data
存储在这个目录下的文件是 App 私有的,其他 App 无法读写(root 用户除外),目录会随着 App 的卸载而被删除
外部存储
外部存储包含私有外部存储和公共目录存储
私有外部存储
私有外部存储是指 /storage/emulated/0/Android/data/com.application.id
我们会在根目录里看到 /sdcard
、/mnt/sdcard
、/storage/emulated/self/primary
下的文件都跟上述的 /storage/emulated/0
中的文件一模一样,这不禁会让人感到疑惑,实际上,通过调研发现这些目录也都是软连接,可以看到其对应实际目录
/sdcard
->/storage/self/primary
/storage/self/primary
->/mnt/user/0/primary
/mnt/user/0/primary
->/storage/emulated/0
所以其实到最后,其目录指向的都是我们的 /storage/emulated/0
目录
在私有外部存储中,App 可以读写自己的目录(/storage/emulated/0/Android/data/com.application.id
)下的文件,如果 Api 大于 19,不需要申请写权限。
如果需要读写其他 App 的私有外部存储目录,则需要声明读写权限,若高于 23,还需要动态进行权限申请。
私有外部存储的目录也会随着 App 的卸载而被删除
写权限 android.permission.WRITE_EXTERNAL_STORAGE
那么为什么会有这样的设计呢?这个 0 又代表什么
我的猜测是 Android 系统中可以有多用户,这个 0 代表了当前用户,如果有第二个用户,应该就会有 1 的出现,使用软连接的方式,会保证在使用 api 获取到相对应的路径时,指向正确的用户下的文件目录,避免多个用户之间的文件系统混乱
当然,这只是我的猜测,未曾验证
公共目录存储
是指 sdcard 中根目录中的公共目录,即 /storage/emulated/0
,例如图片文件夹(/storage/emulated/0/DCIM
),音乐文件(/storage/emulated/0/Music
)
这部分的目录是共享的,所以如果 App 往这个目录下读写文件,需要申请读写权限,并且在 App 卸载后不会被删除。
那我们接着看 Api 的使用
获取内部存储目录
无需申请权限
Context.getFilesDir()
获取内部存储中 files 目录
/data/data/com.application.id/filesContext.getCacheDir()
获取内部存储中 cache 目录
/data/data/com.application.id/cacheContext.getDataDir()//Api >= 24
获取内部存储的存储目录的绝对路径
/data/data/com.application.id
获取外部私有存储目录
无需申请权限
Context.getExternalFilesDir(String type)
获取外部私有存储中的 files 目录或其子文件夹
/storage/emulated/0/Android/data/com.application.id/files
or
/storage/emulated/0/Android/data/com.application.id/files/typeContext.getExternalCacheDir()
获取外部私有存储中的 cache 目录
/storage/emulated/0/Android/data/com.application.id/cache
获取公有目录
读写需要权限
写入权限 android.Manifest.permission#WRITE_EXTERNAL_STORAGE
读取权限 android.Manifest.permission#READ_EXTERNAL_STORAGE
对应的 API
Environment.getExternalStorageDirectory()
获取公有目录
/storage/emulated/01
2
3
4
5
6
7
8
9
10
11
12
13/**
* type
* #DIRECTORY_MUSIC
* #DIRECTORY_PODCASTS
* #DIRECTORY_RINGTONES
* #DIRECTORY_ALARMS
* #DIRECTORY_NOTIFICATIONS
* #DIRECTORY_PICTURES
* #DIRECTORY_MOVIES
* #DIRECTORY_DOWNLOADS
* #DIRECTORY_DCIM
* #DIRECTORY_DOCUMENTS
*/Environment.getExternalStoragePublicDirectory(String type)
获取公有目录下对应的类型文件夹
/storage/emulated/0/DCIM 等
Android 10 分区存储机制
然而,在 Android 10(Api 29) 上,我们发现通过 Environment
获取路径的 api 已经被标记为 Deprecated 的了
这…可咋整呢
其实,这对于 Android 用户来说,是一件好事来着。
随着 Android 的发展,Google 对用户的隐私越来越看重了,慢慢地收紧了开发者对用户设备 sdcard 的读写权限
从 Android 10 开始,对于 Target Api 为 29 的应用,根据官方文档所描述,其访问权限范围限定为外部存储,即分区存储(Scoped Storage)
简单来说,应用只能通过访问Context.getFilesDir()
等 api 访问自己的私有目录(/data/data/packagename/
),以及通过Context.getExternalFilesDir("")
等 api 访问外部存储中自己应用的目录(/Android/data/packagename/
),无需申请权限,这个行为同之前一样,没有变动。
在 Target api < 29
时,只要应用获取到了 WRITE_EXTERNAL_STORAGE
权限,就可以对整个 sdcard 目录进行读取,包括其他应用的 外部私有存储目录(/Android/data/otherAppPackageName/)
但是,在 Target Api >=29
后,在 Android 10 设备上全新安装的应用,即便应用获得了WRITE_EXTERNAL_STORAGE
权限后,应用也无法直接通过 Java File Api
(例如 Environment.getExternalStorageDirectory()
) 对 sdcard 中的非自己应用创建的文件进行读写操作。
这里的全新安装加了着重提示,应用是从 Target Api = 28 覆盖安装升级到 Target Api 29 的话,即便是安装在 Android 10 的手机上,若获得了
WRITE_EXTERNAL_STORAGE
权限,通过 Java File Api 仍然能够对 sdcard 中的任意文件进行读写操作
那…问题来了,在 Target Api >= 29 上
- 应用自身需要将多媒体文件进行存储读取,该怎么做呢。
- 需要访问用户其他 APP 存储的文件(例如照片,视频),又该如何适配呢
在 Android 的规范中,如果用户需要保存多媒体文件到手机中,应保存到共享目录(Share storage)中,以便其他应用访问,例如音乐应用中用户下载的音乐,拍照应用用户拍摄的照片,视频等
下面的表格总结了以上的内容,而至于如何通过 MediaStore Api 和 Storage Access Framework 进行增删查改,我们下文再续
下文来啦:
Android 存储访问框架 Storage Access Framework
如有错误,望各位斧正
Google 官方文档:
Android 10 中的隐私权变更
将文件保存到外部存储
管理分区外部存储访问
Data and file storage overview
使用存储访问框架打开文件
Overview of shared storage
本文参考文章:
感谢各位大大的分享
关于 Android 的文件存储目录
https://ppting.me/2020/04/12/2020_04_12_about_android_file_path/