处理文件系统是任何移动应用程序的基础。每个平台都提供自己的访问模型:iOS 的 Sandbox 将应用程序隔离在单独的容器中,而 Android 的 Scoped Storage 限制对共享存储的直接访问。根据 Google Developer Documentation (2026),Android 10 引入 Scoped Storage 需要对数据存储架构进行彻底重新审视。在本指南中,我们将介绍两个平台的 FileManager、MediaStore API、Storage Access Framework 和 DocumentProvider。
要点
移动应用程序中的文件系统是一组 API、安全规则和约束,定义了数据在 iOS 和 Android 设备上的存储和访问方式。与桌面操作系统不同,移动平台隔离每个应用程序,以保护用户数据不被其他程序未经授权读取。
iOS 使用 Sandbox 模型,每个应用程序在自己的容器中运行,权限受到严格限制。在版本 10 之前,Android 提供对外部存储的完全访问,但随着 Scoped Storage 的引入,这种方法更接近 iOS。主要区别在于 iOS 完全隔离文件系统,而 Android 提供多个访问级别:私有目录、公共 MediaStore 和通过 SAF 的临时访问。
移动应用程序使用三种类型的数据存储。私有存储 — 仅应用程序可访问的目录,用于内部文件和缓存。共享存储 — 通过 MediaStore(Android)或 Files App(iOS)的媒体文件。云存储 — iCloud Drive 和 Google Drive,用于设备间同步。每种类型在大小、文件生命周期和访问条件方面都有自己的限制。
| 存储类型 | iOS | Android |
|---|---|---|
| 私有 | Documents, Library, Caches | getFilesDir(), getCacheDir() |
| 共享媒体 | PHPhotoLibrary 通过选择器 | MediaStore API (ContentResolver) |
| 共享文档 | Files App 通过 UIDocumentPicker | Storage Access Framework (SAF) |
| 云 | iCloud Drive (UIDocument) | Google Drive API |
| 缓存 | Caches 目录,由系统清理 | getCacheDir(), getExternalCacheDir() |
Sandbox 是 iOS 的安全架构,隔离每个应用程序。应用程序只能在其自己的沙盒内读取和写入。要访问联系人、照片或其他应用程序的文件,必须使用系统选择器:UIImagePickerController 或 UIDocumentPickerViewController。对 Files App 的访问通过 Info.plist 中的 UIFileSharingEnabled 标志进行配置。iOS 移动开发中的文件系统需要理解目录结构并为每种数据类型选择正确的位置。
iOS 沙盒由几个标准目录组成。Documents — 用于用户文件,包含在 iCloud Backup 中。Caches — 用于系统在存储空间不足时可能删除的临时数据。Temporary — 用于当前会话的文件,重启时清除。Application Support — 用于对用户隐藏的内部应用程序数据。选择错误的目录会导致问题:在 Documents 中保存缓存会浪费 iCloud 空间并违反 Apple 的文件系统指南。
import Foundation
let fileManager = FileManager.default
guard let documentsURL = fileManager.urls(
for: .documentDirectory,
in: .userDomainMask
).first else { return }
let fileURL = documentsURL.appendingPathComponent("notes.txt")
let text = "Содержимое файла"
// 带加密的原子写入
try text.write(
to: fileURL,
atomically: true,
encoding: .utf8
)
FileManager 类提供了一套完整的方法来管理 iOS 上的文件。FileManager.default 是一个线程安全的单例,适用于大多数操作。诸如 fileExists(atPath:)、createDirectory(at:withIntermediateDirectories:attributes:)、copyItem(at:to:) 和 removeItem(at:) 等方法涵盖了基本场景。超过 1 MB 的文件操作应通过 DispatchQueue.global() 在后台线程中执行。对于大量数据的流式传输,请使用 FileHandle 而不是将整个文件加载到内存中。
func readDocumentsFile(named fileName: String) -> String? {
guard let docsURL = FileManager.default.urls(
for: .documentDirectory,
in: .userDomainMask
).first else { return nil }
let fileURL = docsURL.appendingPathComponent(fileName)
return try? String(contentsOf: fileURL)
}
随着 Android 10 的发布,Google 引入了 Scoped Storage — 一种对文件系统的受限访问模型。应用程序只能在其私有目录中自由读取和写入。对于媒体文件(照片、视频、音频),通过 ContentResolver 使用 MediaStore API。对于任意文档,通过 Intent ACTION_OPEN_DOCUMENT 使用 Storage Access Framework。在 Android 11+ 上,完全禁止直接访问外部存储的根目录,所有开发者必须使用新的 API。
MediaStore 是一个系统 ContentProvider,用于访问设备上的媒体文件。通过 ContentResolver,应用程序请求文件的 Uri 而不是直接路径。MediaStore.Files — 用于所有文件类型,Images — 用于图片,Video — 用于视频,Audio — 用于录音。写入共享目录通过 insert() 进行,指定 DISPLAY_NAME、MIME_TYPE 和 RELATIVE_PATH。插入后,应用程序接收一个 Uri,通过该 Uri 写入字节。MIME 类型起着关键作用 — 不正确的类型在打开文件时会导致错误。
val contentValues = ContentValues().apply {
put(MediaStore.MediaColumns.DISPLAY_NAME, "report.pdf")
put(MediaStore.MediaColumns.MIME_TYPE, "application/pdf")
put(MediaStore.MediaColumns.RELATIVE_PATH, "Documents/Reports")
}
val uri = contentResolver.insert(
MediaStore.Files.getContentUri("external"),
contentValues
)
uri?.let {
contentResolver.openOutputStream(it)?.use { stream ->
stream.write(pdfBytes)
}
}
SAF 提供了一个统一界面,用于在无需运行时权限的情况下选择和创建文件。Intent ACTION_OPEN_DOCUMENT 打开 Android 上的系统文件管理器。选择后,应用程序接收一个 content:// Uri,具有通过 FLAG_GRANT_READ_URI_PERMISSION 的临时访问权限。ACTION_CREATE_DOCUMENT 允许将文件保存到用户选择的任何外部存储位置。SAF 在 Android 5+ 上运行,并提供对通过 DocumentsProvider 连接的云提供商文件的访问。
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
addCategory(Intent.CATEGORY_OPENABLE)
type = "*/*"
putExtra(Intent.EXTRA_MIME_TYPES, arrayOf(
"application/pdf",
"text/plain"
))
}
startActivityForResult(intent, REQUEST_CODE)
两个平台都提供内置机制供用户选择文件。移动应用程序中的文档通过系统选择器传递,这些选择器提供对文件的临时访问,无需永久权限。在 iOS 上是 UIDocumentPickerViewController,在 Android 上是 ACTION_OPEN_DOCUMENT。移动应用程序中的文档可以从本地存储和云服务中选择。用户明确指定文件,应用程序接收具有有限有效期的 Uri 或 URL。
UIDocumentPickerViewController 打开 Files App,并允许选择一个或多个文档。模式:导入(复制到沙盒)和打开(通过安全作用域 URL 访问)。对于文件过滤,传递一个 UTType 类型数组 — 例如 .pdf 和 .plainText。收到 URL 后,应用程序必须在读取前调用 startAccessingSecurityScopedResource(),并在完成后调用 stopAccessingSecurityScopedResource()。不调用 stopAccessing 会导致系统资源泄漏。iOS 上的移动应用程序中的文档需要在完成文件操作后强制释放临时权限。
let picker = UIDocumentPickerViewController(
forOpeningContentTypes: [.pdf, .plainText]
)
picker.allowsMultipleSelection = true
picker.delegate = self
present(picker, animated: true)
// 在委托中释放访问
func documentPicker(
_ controller: UIDocumentPickerViewController,
didPickDocumentsAt urls: [URL]
) {
guard let url = urls.first else { return }
url.startAccessingSecurityScopedResource()
defer { url.stopAccessingSecurityScopedResource() }
}
FileProvider 是 ContentProvider 的子类,用于在应用程序之间安全共享文件。它基于指定 XML 目录中的文件生成临时的 content:// Uri。其他应用程序通过带有 FLAG_GRANT_READ_URI_PERMISSION 的 Intent 获取访问权限。DocumentProvider 与 FileProvider 不同,它在 SAF 中发布文件,并允许其他应用程序将您应用程序的内容作为文件系统的一部分浏览。要实现 DocumentsProvider,您需要覆盖 queryRoots()、queryChildDocuments() 和 openDocument(),然后在 AndroidManifest.xml 中注册。
云同步使用户能够在其所有设备上访问文档。移动应用程序中的文件系统通过云层得到增强:iOS 上的 UIDocument 自动跟踪更改并通过 iCloud 同步。在 Android 上,类似的功能通过 Google Drive API 或具有云根的 DocumentsProvider 构建。理解移动开发中的文件系统对于构建可靠的跨设备同步至关重要。
UIDocument 是用于处理 iCloud 文档的抽象类。它自动保存更改、读取数据并通知委托有关更新。在写入冲突时,NSFileVersion 提供可用版本列表 — 开发者可以选择最新版本或向用户显示冲突解决选项。在项目的 Capabilities 中配置 Ubiquity Container 是 iCloud Drive 所必需的。NSFileCoordinator 和 NSFilePresenter 防止多线程或多设备同时访问时的数据竞争。
iOS 自动将 Documents 目录包含在 iCloud Backup 中。Android 使用 Auto Backup for Apps — 系统将 getFilesDir()、SharedPreferences 和 SQLite 数据库中的数据保存到 Google Drive。缓存和外部文件不包括在备份中。两个平台都允许配置例外:在 iOS 上通过 NSURLIsExcludedFromBackupKey,在 Android 上通过 XML 备份规则配置。包含个人数据的文件加密是强制性的 — 在 iOS 上使用 NSDataWritingFileProtectionComplete,在 Android 上使用 security-crypto 库中的 EncryptedFile。
| 参数 | iOS | Android |
|---|---|---|
| 默认备份 | Documents 和 Library | getFilesDir(), SharedPreferences, 数据库 |
| 文件例外 | isExcludedFromBackupKey | XML 备份规则 (fullBackupContent) |
| 加密 | NSDataWritingFileProtectionComplete | EncryptedFile (security-crypto) |
| 云同步 | UIDocument + iCloud | Google Drive API + SAF |
| 自动恢复 | 安装后 iCloud Restore | 重新安装时 Auto Backup |
常见问题
Sandbox 是 iOS 上每个应用程序的隔离环境。如果不使用 UIDocumentPickerViewController 等系统选择器,应用程序无法访问其他应用程序的文件。
Scoped Storage 是 Android 10+ 上受限的文件系统访问模型。应用程序直接只读取自己的文件,媒体使用 MediaStore API,文档使用 Storage Access Framework。
使用 UIDocumentPickerViewController — 用于从 Files App 或 iCloud Drive 选择文档的系统选择器。选择后,您将获得一个具有临时访问权限的安全作用域 URL。
对于媒体文件,通过指定 MIME 类型的 ContentResolver 使用 MediaStore API。对于任意文档,使用带有 Intent ACTION_OPEN_DOCUMENT 的 Storage Access Framework。
FileProvider 是 ContentProvider 的子类,用于通过带有 FLAG_GRANT_READ_URI_PERMISSION 的临时 content:// Uri 在应用程序之间安全共享文件。
总结
我们将开发一款交钥匙移动应用程序
IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。