一、总结(Android10之前)

android 存储设备 android 存储空间分析_存储空间

路径取得方法

APP专用

非root用户可见

APP卸载时

内部数据空间

Context.getFilesDir()

yes

no

消失

内部缓存空间

Context.getCacheDir()

yes

no

消失

外部数据空间

Context.getExternalFilesDir(String type)

yes

yes

消失

外部缓存空间

Context.getExternalCacheDir()

yes

yes

消失

外部公开空间

Environment.getExternalStorageDirectory()

no

yes

不消失

外部公开共享空间

Environment.getExternalStoragePublicDirectory(String type)

no

yes

不消失

内部空间 只有APP自己可以访问,其他APP和手机用户都不可以见。卸载消失。

外部私有空间 APP自己,手机用户,其他授权APP可以访问。卸载消失

外部公开空间,所有APP和用户都可以访问。卸载不消失。

测试:

Context.getFilesDir()                           :/data/data/com.example/files
Context.getCacheDir()                           :/data/data/com.example/cache
Context.getExternalCacheDir()                   :/storage/emulated/0/Android/data/com.example/cache
Context.getExternalFilesDir()                   :/storage/emulated/0/Android/data/com.example/files
Environment.getExternalStorageDirectory()       :/storage/emulated/0
Environment.getExternalStoragePublicDirectory() :/storage/emulated/0/

其中,除了外部存储公共空间以外,其他目录无需存储权限就可访问。


1.内部存储空间中的应用私有目录

路径取得方法

APP专用

非root用户可见

APP卸载时

内部数据空间

Context.getFilesDir()

yes

no

消失

内部缓存空间

Context.getCacheDir()

yes

no

消失

对于设备中每一个安装的 App,系统都会在内部存储空间的 data/data 目录下以应用包名为名字自动创建与之对应的文件夹。这个文件夹用于 App 中的 WebView 缓存页面信息,SharedPreferences 和 SQLiteDatabase 持久化应用相关数据等。

对于没有 Root 过的手机,普通用户是无法查看 data/data 目录内容的。不过开发人员可以使用模拟器调试应用,并通过 DDMS(Dalvik Debug Monitor Server)提供的 File Explorer 工具查看模拟器设备的存储空间。

当设备的内部存储空间不足时,Android 可能会删除Context.getCacheDir()下的文件以回收空间。

当用户卸载 App 时,系统自动删除 data/data 目录下对应包名的文件夹及其内容。

2.外部存储空间中的应用私有目录

路径取得方法

APP专用

非root用户可见

APP卸载时

外部数据空间

Context.getExternalFilesDir()

yes

yes

消失

外部缓存空间

Context.getExternalCacheDir()

yes

yes

消失

考虑内部存储空间容量有限,普通用户不能直接直观地查看目录文件等其他原因,Android 在外部存储空间中也提供有特殊目录供应用存放私有文件,文件路径为:

/storage/emulated/0/Android/data/app包名目录

值得注意的是,与内部存储空间的应用私有目录不同的是:

  • 默认情况下,系统并不会自动创建外部存储空间的应用私有目录。只有在应用需要的时候,开发人员通过 SDK 提供的 API 创建该目录文件夹和操作文件夹内容。
  • 自 Android 7.0 开始,系统对应用私有目录的访问权限进一步限制。其他 App 无法通过 file:// 这种形式的 Uri 直接读写该目录下的文件内容,而是通过 FileProvider 访问。
  • 系统媒体扫描程序不会读取这些目录中的文件,但是其他应用依然可以对该目录下的文件读写,普通用户可以自由修改和删除,开发人员在使用时,一定要做好判空处理和异常捕获,防止应用崩溃退出!
  • Context.getExternalCacheDir()当内置存储的空间不足时将系统自动被清除
  • Android 4.4以后访问该目录不再需要权限了

而相同点在于:同属于应用私有目录,当用户卸载 App 时,系统也会自动删除外部存储空间下的对应 App 私有目录文件夹及其内容。

3.外部存储空间中的公共目录

路径取得方法

APP专用

非root用户可见

APP卸载时

外部公开空间

Environment.getExternalStorageDirectory()

no

yes

不消失

外部公开共享空间

Environment.getExternalStoragePublicDirectory()

no

yes

不消失

从访问权限上来说,应用无关数据应该是宿主应用希望与其他应用共享这些数据,应该存放在外部存储空间的公共目录文件夹下。

外部存储空间已经为用户默认分类出一些公共目录。开发人员可以通过 Environment 类提供的方法直接获取相应目录的绝对路径,传递不同的 type 参数类型即可:

Environment.getExternalStoragePublicDirectory(String type);

Envinonment 类提供诸多 type 参数的常量,比如:

  • DIRECTORY_MUSIC:/storage/emulated/0/Music
  • DIRECTORY_MOVIES:/storage/emulated/0/Movie
  • DIRECTORY_PICTURES:/storage/emulated/0/Pictures
  • DIRECTORY_DOWNLOADS: /storage/emulated/0/Download
  • DIRECTORY_DOCUMENTS: /storage/emulated/0/Documents

还可以在外部存储空间自由创建其他目录,通过这个方式获取外部存储空间的绝对路径,然后操作文件:

Environment.getExternalStorageDirectory();

有个小技巧,无论外部内部,只要路径中有包名,那么就是私有的,而且是随着程序的卸载而被删除的,有包名的路径均是Context中的方法,而公有的路径均是Environment调用的,卸载不会删除。

二、Android 10和Android 11

Android 10之前,外部存储区的内容主要以两种形式存在,一种是由应用的包名命名,归属于特定应用目录下的内容,另一种是存储在公共存储区域的内容。

在Android 10 中,Google首次引入了分区存储,将公共区域划分成了不同的集合,并且在媒体文件和其他文档之间建立了清楚的分割。经过划分之后应用不可以随意访问外部存储区中的文件,而只能访问媒体文件。如果想访问包含更多细节数据的其他文档,应用专门向用户申请有关文档的访问权限。

android 存储设备 android 存储空间分析_android 存储设备_02

分区存储是需要以 Android 10 为目标平台的,系统默认强制执行。如果在 AndroidManifest 中添加了 requestLegacyExternalStorage=true 标志,就可以不受此限制。但是当 target API 更新为 30 后,系统会忽略该配置。

如果有数据需要迁移,可以在 AndroidManifest 中将 preserveLegacyExternalStorage 属性设为 true ,当用户升级到以 Android 11 为目标平台时,这个配置就会生效。具体而言,这个配置在用户重新安装该应用前都是有效的。

android 存储设备 android 存储空间分析_Android_03

针对以 Android 11 为目标平台的应用 (targetSdkVersion = 30) ,WRITEEXTENRNALSTORAGE 和 WRITEMEDIASTORAGE 不再提供其他任何访问权限 。

某些应用的核心功能可能需要访问大量的文件,例如文件管理操作、备份和恢复操作等等,此时就需要申请 MANAGEEXTERNALSTORAGE 权限。我们可以通过使用 ACTIONMANAGEALLFILESACCESS_PERMISSION intent 操作将用户引导至一个系统设置页面,让用户为应用授予所有文件的管理权限。