在开发Android应用的过程中,常常需要用到数据持久化技术,Android操作系统提供了内部存储外部存储两种方式,分别对应文件系统下不同的存储目录,可以参考官方文档https://developer.android.com/training/data-storage,下面介绍二者的用途和区别。

内部存储

内部存储(internal storage)就是Android手机ROM上的一块特定存储区域,主要用于存储系统以及应用程序的数据。其在文件系统中对应的根目录是/data/data/(/data/app 目录下是所有安装的apk文件),用户需要root权限才可以访问。每个应用的内部存储目录都是私有的,在/data/data下按照应用的package_name进行目录划分,每个应用仅能访问内部存储中自己的特定文件夹,且不需要申请权限。每个应用的内部存储目录会安装应用时自动创建,并在应用卸载时自动删除。一个应用的内部存储目录一般包含如下文件夹:

  • files
    主要用于存储敏感的持久化的自定义数据,java接口getFilesDir获取,NDK C++接口ANativeActivity.internalDataPath获取。
  • cache
    主要用于存储缓存数据,java接口getCacheDir获取。
  • app_webview
    主要用于存储webview加载过程中的数据,如Cookie,LocalStorage等。
  • databases
    主要用于存储数据库类型的数据。
  • shared_prefs
    用于存储SharedPreference文件。

我们会发现在代码中获取的内部存储根目录是/data/user/0/,而不是前面提到的/data/data,是因为Android 4.2开始为了支持多用户,同时为了兼容旧应用,将/data/data/直接链接到当前用户文件夹(默认是用户0的文件夹/data/user/0/)。

外部存储

在Android 4.4之前,外部存储(external storage)就是指代SD卡存储,内部存储就是指代手机自带的存储;但是在Android 4.4以后,对于不支持sd卡扩展的设备,外部存储和内部存储都位于手机机身存储上,只是同一个存储介质上的不同存储区域。在Android手机的文件管理工具下能够看到的目录就是外部存储目录,可以通过USB方式与外部存储中的数据进行交互,也就是说外部存储对于用户来说是可读写的。外部存储细分为两种类型:外部私有存储外部公共存储。外部私有存储的根目录在/storage/emulated/0/Android/data/,和内部存储类似,该目录也是通过应用的package_name进行目录划分,每个应用默认拥有其对应包名下的文件夹的访问权限,应用删除时也会随之删除其对应的外部私有存储目录。应用的外部私有存储目录一般包含如下文件夹:

  • files
    主要用于存储常规的持久化的自定义数据,java接口getExternalFilesDir获取,NDK C++接口ANativeActivity.externalDataPath获取。
  • cache
    主要用于存储缓存数据,java接口getExternalCacheDir获取。

至于外部公共存储,也叫共享存储,其根目录一般是/storage/emulated/0,可以通过java接口getExternalStorageDirectory获取,常用的外部公共存储文件夹有DCIM、Pictures、Alarms、Music、Movies、Downloads或应用自定义的任何文件夹。但是对于大部分应用来说,不推荐使用外部公共存储(除非需要和其它应用进行文件数据共享),因为外部公共存储目录最大的特点是该目录下由应用创建的文件不会随着应用的卸载而删除,所以随着时间的推移外部存储空间会逐渐被这类垃圾文件占满而难以清理,可以说外部公共存储的存在是android文件系统长期以来管理混乱的最大罪恶源头,只要应用申请了WRITE_EXTERNAL_STORAGE和READ_EXTERNAL_STORAGE权限就可以访问外部公共存储的任意目录。下面是在旧存储模型(api level<29)下两种存储的对比:

旧存储模型 内部存储 外部私有存储 外部公共存储
存储类型 私有存储 私有存储 共享存储
路径 /data/user/0/package/ /storage/emulated/0/An droid/data/package/ /storage/emulated/0
生命周期 和宿主app相同 和宿主app相同 永久
宿主app访问权限 默认可访问 默认可访问 需要EXTERNAL_STORAGE权限
宿主app访问方式 文件系统 文件系统 文件系统
其它app访问权限 无权限访问 需要EXTERNAL_STORAGE权限 /
其它app访问方式 / 7.0之前文件系统
7.0之后FileProvider(主动分享)
/
用户访问权限 需要root权限 默认可访问 默认可访问
主要用途 敏感且少量的数据 常规且大量的数据 用于分享的公共数据

分区存储模型

Google为了让用户更好地管理自己的文件并减少混乱,在Android 10(api level>=29)后构建的引用默认使用分区存储模型(scoped storage),使用该存储模型的应用只能访问外部存储空间上的应用私有目录和本应用所创建的特定类型的媒体文件。Android系统的版本越新,就越依赖于文件的用途而不是位置来确定应用对特定文件的访问和写入能力,这种基于用途的存储模型可增强用户隐私保护,因为应用只能访问其在设备文件系统中实际使用的区域。Android 10仍然提供了兼容旧存储模型的方式,但是在Android 12后构建的应用强制执行分区存储模型,下面是启用分区存储模型后各种存储的访问限制:

分区存储模型 内部存储 外部私有存储 外部公共存储(媒体库) 外部公共存储(其它)
宿主app访问权限 默认可访问 默认可访问 自身创建的文件:默认可访问
其它应用的媒体文件:需要EXTERNAL_STORAGE权限
需要MANAGE_EXTERNAL_STORAGE权限
宿主app访问方式 文件系统 文件系统 文件系统
MediaStore API
文件系统
其它app访问权限 无权限访问 FileProvider(主动分享) / /
用户访问权限 需要root权限 默认可访问 默认可访问 默认可访问