Android 中有4种可使用的不同数据存储选项:
应用不需要任何系统权限即可读取和写入应用内部目录。默认情况下,保存至内部存储的文件是应用私有文件,其他应用(和用户)不能访问这些文件(除非拥有 Root 访问权限)。当用户卸载应用时,保存在内部存储中的文件也将随之移除。 在真机(Android 10)上尝试获取应用的内部文件存储目录:
context.getDataDir(): /data/user/0/com.package.name
context.getFilesDir():/data/user/0/com.package.name/files
context.getCacheDir(): /data/user/0/com.package.name/cache
context.getCodeCacheDir(): /data/user/0/com.package.name/code_cache
context.getNoBackupFilesDir(): /data/user/0/com.package.name/no_backup
应用不应该直接使用通过getDataDir()
获得的路径,而应使用getFilesDir()
或getCacheDir()
。
getFilesDir()
返回表示应用内部目录的File
。调用context.openFileOutput()
以获取会写入内部目录中的文件的FileOutputStream
。调用context.openFileInput(name)
并传入文件名可以打开现有文件。getCacheDir()
返回表示应用临时缓存文件的内部目录的File
。如果需要缓存某些文件,则应使用File.createTempFile()
。如果系统的存储空间不足,则可能会在不发出警告的情况下删除缓存文件。但是,不应依赖系统清理这些文件,而应始终自行维护缓存文件,使其占用的空间保持在合理的限制范围内。Android 包含以下访问外部存储中的文件的权限:
READ_EXTERNAL_STORAGE
允许应用访问外部存储设备中的文件。WRITE_EXTERNAL_STORAGE
允许应用在外部存储设备中写入和修改文件。拥有此权限的应用也会自动获得 READ_EXTERNAL_STORAGE
权限。从 Android 4.4(API 级别 19)开始,在特定于应用的目录中读取或写入文件不再需要任何与存储相关的权限。如果应用使用分区存储,则除非应用需要访问其他应用创建的文件,否则无需声明任何与存储相关的权限。
在请求存储权限并确认存储可用后,可以保存以下类型的文件:
指可供其他应用和用户自由访问的文件。在用户卸载应用后,这些文件仍然可供用户使用。
如果要将文件保存到其他应用可以访问的外部存储上,使用以下 API 之一:
MediaStore
API。ACTION_CREATE_DOCUMENT
intent,这是存储访问框架的一部分。如果不希望媒体扫描程序发现文件,需在特定于应用的目录中添加名为.nomedia
的空文件(注意文件名中的句点前缀)。这可以防止媒体扫描程序读取应用的媒体文件并通过MediaStore
API 将它们提供给其他应用。
指存储在特定于应用的目录中的文件(使用Context.getExternalFilesDir()
来访问)。这些文件在用户卸载应用时会被清除。 尽管这些文件在技术上可被用户和其他应用访问(因为它们存储在外部存储上),但它们不能为应用之外的用户提供价值。可以使用此目录来存储不想与其他应用共享的文件。在真机(Android 10)上尝试获取应用的外部文件存储目录:
context.getObbDir(): /storage/emulated/0/Android/obb/com.package.name
context.getExternalMediaDirs()[0]: /storage/emulated/0/Android/media/com.package.name
context.getExternalCacheDir(): /storage/emulated/0/Android/data/com.package.name/cache
context.getExternalFilesDir(null): /storage/emulated/0/Android/data/com.browser.hq/files
context.getExternalFilesDir(Environment.DIRECTORY_MUSIC):/storage/emulated/0/Android/data/com.package.name/files/Music
通过调用getExternalFilesDir()
并传入指明想要的目录类型的名称来获取特定于应用的目录,务必使用由DIRECTORY_PICTURES
这类 API 常量提供的目录名称。这些目录名称可确保系统正确地处理这些文件。例如,保存在DIRECTORY_RINGTONES
中的文件会被系统媒体扫描程序归类为铃声,而不是音乐。如果没有适合的预定义子目录名称,可以调用getExternalFilesDir()
并传入null
,这会返回应用在外部存储上的专用目录的根目录。
context.getExternalFilesDirs()
会返回一个File
数组,因为这里的外部存储目录除了永久性存储空间的“外部”分区外,还可能会有可移动存储媒介(如 Micro SD 卡)。数组中的第一个条目被视为主要外部存储,除非该位置已满或不可用,否则应该一律使用该位置。无论外部存储空间是否可移动,这两种存储空间的 API 行为都是相同的。
为了让用户更好地管理自己的文件并减少混乱,以Android 10(API 级别 29) 及更高版本为目标平台的应用在默认情况下被赋予了对外部存储设备的分区访问权限(即分区存储)。以 Android 9 或更低版本为目标平台的应用,可以通过将android:requestLegacyExternalStorage
的值设为false
来选择启用分区存储行为。除非应用需要访问存放在应用的专有目录以及MediaStore
之外的文件,否则最好使用分区存储。
使用分区存储时,应用保存和访问自己创建的文件:
READ_EXTERNAL_STORAGE
或WRITE_EXTERNAL_STORAGE
权限。Context.getExternalFilesDir()
访问)或者MediaStore
。应用访问其他应用创建的文件:
READ_EXTERNAL_STORAGE
权限。MediaStore.Images
中。MediaStore.Video
中。MediaStore.Audio
中。应用访问其他应用创建的任何其他文件:
/sdcard/DCIM/IMG1024.JPG
这类路径不具有直接内核访问权限,即使拥有READ_EXTERNAL_STORAGE
权限。要访问此类文件,应用必须使用MediaStore
,并调用openFile()
等方法。如果应用尝试通过原始文件系统视图打开这些文件,则会发生FileNotFoundException
错误。参考:
数据和文件存储概览 | Android 开发者 | Android Developers
└─ 将文件保存到设备存储空间中 | Android 开发者 | Android Developers
├─ 将文件保存到内部存储空间中 | Android 开发者 | Android Developers
└─ 将文件保存到外部存储 | Android 开发者 | Android Developers
└─ 管理分区外部存储访问 | Android 开发者 | Android Developers
以上是基于Android开发者官方中文版文档来进行整理的,然而官方英文版文档对Android数据存储的分类却不太一样。按英文版文档,系统同样提供了4个选项来保存应用程序数据:
其实这种分类方法与上述的分类方法大同小异,只不过将内部文件存储以及外部文件存储中的私有文件归类为应用专属存储,而外部文件存储中的公开文件归为共享存储。
参考:
Data and file storage overview | Android 开发者 | Android Developers
└─ Access app-specific files | Android 开发者 | Android Developers