# 介绍

截至本文编写的时间(2023-03-06),在 Android 系统中应用对公共存储空间的滥用仍然是一个没有解决的问题。

存储空间隔离致力于在尽可能小或不影响应用功能的前提下,解决这一问题。

要清楚地说明这个问题,我们不得不先引入一些技术性的概念。

# Android 系统对于存储空间的设计

Android 系统为应用提供了两大类共三处存储其文件的位置,它们分别为:

# data 区域

对应的文件夹:

  • /data/user/<user_id>/<package_name>
  • /storage/emulated/<user_id>/Android/data/<package_name>

<user_id> 为用户 ID,涉及 Android 的多用户/工作资料机制,与本文主题无关。

<package_name> 为应用的唯一 ID,即俗称的“包名”。

这两个文件夹应该用于存储应用自身的数据,具有如下特点:

  • 应用无需请求权限即可使用
  • 只有应用自身可以访问
  • 在卸载应用或清除应用数据后会被删除

/storage/emulated/<user_id>/Android/data/<package_name>/data/user/<user_id>/<package_name> 的用处相同。它的存在是因为一些历史原因:在 Android 4.x 及更早的时代,设备自带的存储空间通常非常小,外置 SD 卡会被用来扩展存储空间,此处便也被用作存储应用自身的数据。

# 内部存储空间

对应的文件夹:

  • /storage/emulated/<user_id>

此处应存放对用户有用的文件,例如用户在应用中保存的图片。Android 系统提供了 DCIMDownloadPictures 等公共文件夹用于分门别类地保存照片、下载的文件等。此处还可以被成为“共享存储空间”、“公共存储空间”等。

具有如下特点:

  • 需要存储权限才可以读取
  • 需要存储权限才可以写入〔1〕
  • 拥有存储权限的应用可以读取其中所有的文件,包括由其他应用、用户写入的文件
  • 在卸载应用或清除应用数据后,应用写入的文件不会被删除

〔1〕 在 Android 11 后有变化,请参考下文

# 应用访问文件的方式

除了最基本的直接通过文件路径访问以外,Android 系统还提供了其他几种方式。

  • 媒体存储

    媒体存储是一个系统应用,其内部有一个包含内部存储空间中所有文件信息的数据库。最常见的用例是,从媒体存储查询所有的图片文件。读取、写入文件也可以通过媒体存储进行。

  • 存储访问框架(Storage Access Framework,简称 SAF)

    SAF 是一个 Android 4.4 起添加的功能。借助 SAF,用户可以从一个统一的由系统提供的 UI 进行打开文件、保存文件等操作。应用程序还可以自己成为一个提供程序,其他使用 SAF 的应用便可使用它。

    SAF 的好处是应用仅可使用由用户选择的文件、统一的 UI、应用无需请求权限等等。

    但是,多数应用还是更倾向于请求存储权限后自己实现需要的功能。

# 什么是应用滥用存储空间的问题

对存储空间的滥用是对“内部存储空间”的滥用。 一些应用或一些 SDK 会希望自己的数据文件在卸载后不会被删除,因此它们会选择“内部存储空间”写入其数据文件。

如果应用有“发送图片”、“保存文件”等功能,用户很多时候就不得不授予应用存储权限。它们就会借此在“内部存储空间”写入一大堆奇奇怪怪的文件夹(见下图)。久而久之用户的存储空间将变得混乱无比。

例子

许多滥用存储空间的应用建立一堆奇奇怪怪的文件夹,甚至以“SystemConfig”命名来让用户误以为是系统文件。

例子

# Android 11 添加的分区存储(Scoped storage)

许多人认为分区存储能够解决这个问题,但是事实并非如此。

受分区存储限制的应用在使用公共存储空间时的行为会发生如下的变化:

  • 仅可在公共文件夹中写入对应类型的文件(但是系统只会检查文件名称是否符合规则)
  • 不需要任何权限即可在公共文件夹中写入文件(这比以前更加宽松!)

显然我们可以发现,仍然可以写入任意文件,唯一的区别仅仅是需要调整保存文件的位置到某个公共文件中,调整文件名来欺骗系统。

此外,分区存储仅作用于以 Android 11 或以上版本为目标平台的应用(即 Target API ≥ 31)。不在 Google Play 上架的应用或停止更新的老应用不会受到限制。

# 解决这个问题

为了解决上面这个问题,我们创造了这个应用——存储空间隔离。

用户可以为特定的应用启用隔离。应用所使用的“内部存储空间”实际将变为 /storage/emulated/<user_id>/Android/data/<package_name> 中的一个文件夹。因此,真正的“内部存储空间”将不会被污染,其创建的文件也会在卸载后得以删除。

我们提供了多种机制来保证被隔离的应用在需要使用“内部存储空间”中的文件时工作正常。请阅读后续的文档。