Làm việc với hệ thống tệp là nền tảng của mọi ứng dụng di động. Mỗi nền tảng cung cấp mô hình truy cập riêng: Sandbox trên iOS cô lập các ứng dụng trong các vùng chứa riêng biệt, trong khi Scoped Storage trên Android hạn chế truy cập trực tiếp vào bộ nhớ dùng chung. Theo Google Developer Documentation (2026), việc giới thiệu Scoped Storage với Android 10 đòi hỏi phải xem xét lại toàn bộ kiến trúc lưu trữ dữ liệu. Trong hướng dẫn này, chúng ta sẽ tìm hiểu FileManager, MediaStore API, Storage Access Framework và DocumentProvider cho cả hai nền tảng.
Những điểm chính
Hệ thống tệp trong ứng dụng di động là tập hợp các API, quy tắc bảo mật và ràng buộc xác định cách lưu trữ và truy cập dữ liệu trên thiết bị iOS và Android. Không giống như hệ điều hành máy tính để bàn, nền tảng di động cô lập từng ứng dụng để bảo vệ dữ liệu người dùng khỏi bị đọc trái phép bởi các chương trình khác.
iOS sử dụng mô hình Sandbox, trong đó mỗi ứng dụng tồn tại trong vùng chứa riêng với quyền hạn bị giới hạn nghiêm ngặt. Trước phiên bản 10, Android cung cấp quyền truy cập đầy đủ vào bộ nhớ ngoài, nhưng với sự ra đời của Scoped Storage, cách tiếp cận đã trở nên gần giống với iOS. Sự khác biệt chính là iOS cô lập hoàn toàn hệ thống tệp, trong khi Android cung cấp nhiều cấp độ truy cập: thư mục riêng tư, MediaStore công khai và truy cập tạm thời qua SAF.
Ứng dụng di động sử dụng ba loại lưu trữ dữ liệu. Lưu trữ riêng tư — thư mục chỉ ứng dụng mới có thể truy cập cho các tệp nội bộ và bộ nhớ đệm. Lưu trữ dùng chung — tệp phương tiện qua MediaStore (Android) hoặc Files App (iOS). Lưu trữ đám mây — iCloud Drive và Google Drive để đồng bộ giữa các thiết bị. Mỗi loại có giới hạn riêng về kích thước, thời gian tồn tại của tệp và điều kiện truy cập.
| Loại lưu trữ | iOS | Android |
|---|---|---|
| Riêng tư | Documents, Library, Caches | getFilesDir(), getCacheDir() |
| Phương tiện dùng chung | PHPhotoLibrary qua bộ chọn | MediaStore API (ContentResolver) |
| Tài liệu dùng chung | Files App qua UIDocumentPicker | Storage Access Framework (SAF) |
| Đám mây | iCloud Drive (UIDocument) | Google Drive API |
| Bộ nhớ đệm | Thư mục Caches, hệ thống dọn dẹp | getCacheDir(), getExternalCacheDir() |
Sandbox là kiến trúc bảo mật của iOS cô lập từng ứng dụng. Ứng dụng chỉ có thể đọc và ghi bên trong hộp cát của chính nó. Để truy cập danh bạ, ảnh hoặc tệp từ ứng dụng khác, phải sử dụng bộ chọn hệ thống: UIImagePickerController hoặc UIDocumentPickerViewController. Truy cập Files App được cấu hình qua cờ UIFileSharingEnabled trong Info.plist. Hệ thống tệp trong phát triển di động trên iOS yêu cầu hiểu cấu trúc thư mục và chọn đúng vị trí cho từng loại dữ liệu.
Hộp cát iOS bao gồm một số thư mục tiêu chuẩn. Documents — cho tệp người dùng, được bao gồm trong iCloud Backup. Caches — cho dữ liệu tạm thời mà hệ thống có thể xóa khi dung lượng thấp. Temporary — cho tệp phiên hiện tại, bị xóa khi khởi động lại. Application Support — cho dữ liệu nội bộ của ứng dụng ẩn khỏi người dùng. Chọn sai thư mục dẫn đến vấn đề: lưu bộ nhớ đệm trong Documents lãng phí dung lượng iCloud và vi phạm nguyên tắc hệ thống tệp của 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 = "Содержимое файла"
// Ghi nguyên tử với mã hóa
try text.write(
to: fileURL,
atomically: true,
encoding: .utf8
)
Lớp FileManager cung cấp bộ phương thức hoàn chỉnh để quản lý tệp trên iOS. FileManager.default là một singleton an toàn luồng phù hợp cho hầu hết các thao tác. Các phương thức như fileExists(atPath:), createDirectory(at:withIntermediateDirectories:attributes:), copyItem(at:to:) và removeItem(at:) bao phủ các kịch bản cơ bản. Các thao tác tệp lớn hơn 1 MB nên được thực hiện trong luồng nền qua DispatchQueue.global(). Để truyền phát khối lượng lớn, hãy sử dụng FileHandle thay vì tải toàn bộ tệp vào bộ nhớ.
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)
}
Với việc phát hành Android 10, Google đã giới thiệu Scoped Storage — mô hình truy cập hạn chế vào hệ thống tệp. Ứng dụng chỉ có thể đọc và ghi tự do trong các thư mục riêng tư của nó. Đối với tệp phương tiện (ảnh, video, âm thanh), MediaStore API được sử dụng qua ContentResolver. Đối với tài liệu tùy ý, Storage Access Framework được sử dụng qua Intent ACTION_OPEN_DOCUMENT. Trên Android 11+, quyền truy cập trực tiếp vào thư mục gốc của bộ nhớ ngoài bị cấm hoàn toàn và tất cả nhà phát triển phải sử dụng API mới.
MediaStore là ContentProvider hệ thống để truy cập tệp phương tiện trên thiết bị. Qua ContentResolver, ứng dụng yêu cầu Uri tệp thay vì đường dẫn trực tiếp. MediaStore.Files — cho tất cả loại tệp, Images — cho hình ảnh, Video — cho video, Audio — cho bản ghi âm. Ghi vào thư mục dùng chung được thực hiện qua insert() với DISPLAY_NAME, MIME_TYPE và RELATIVE_PATH. Sau khi chèn, ứng dụng nhận được Uri để ghi byte. Loại MIME đóng vai trò quan trọng — loại sai sẽ gây lỗi khi mở tệp.
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 cung cấp giao diện thống nhất để chọn và tạo tệp mà không cần quyền runtime. Intent ACTION_OPEN_DOCUMENT mở trình quản lý tệp hệ thống trên Android. Sau khi chọn, ứng dụng nhận được Uri content:// với quyền truy cập tạm thời qua FLAG_GRANT_READ_URI_PERMISSION. ACTION_CREATE_DOCUMENT cho phép lưu tệp vào bất kỳ vị trí lưu trữ ngoài nào do người dùng chọn. SAF hoạt động trên Android 5+ và cung cấp quyền truy cập vào tệp từ nhà cung cấp đám mây được kết nối qua 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)
Cả hai nền tảng đều cung cấp cơ chế tích hợp cho người dùng chọn tệp. Tài liệu trong ứng dụng di động được truyền qua bộ chọn hệ thống cấp quyền truy cập tạm thời vào tệp mà không cần quyền vĩnh viễn. Trên iOS, đó là UIDocumentPickerViewController; trên Android, đó là ACTION_OPEN_DOCUMENT. Tài liệu trong ứng dụng di động có thể được chọn từ cả bộ nhớ cục bộ và dịch vụ đám mây. Người dùng chỉ định rõ ràng tệp và ứng dụng nhận được Uri hoặc URL với thời hạn hiệu lực giới hạn.
UIDocumentPickerViewController mở Files App và cho phép chọn một hoặc nhiều tài liệu. Chế độ: import (sao chép vào hộp cát) và mở (truy cập qua URL có phạm vi bảo mật). Để lọc tệp, một mảng loại UTType được truyền — ví dụ: .pdf và .plainText. Sau khi nhận URL, ứng dụng phải gọi startAccessingSecurityScopedResource() trước khi đọc và stopAccessingSecurityScopedResource() sau khi hoàn thành. Không gọi stopAccessing dẫn đến rò rỉ tài nguyên hệ thống. Tài liệu trong ứng dụng di động trên iOS yêu cầu giải phóng bắt buộc các quyền tạm thời sau khi hoàn thành làm việc với tệp.
let picker = UIDocumentPickerViewController(
forOpeningContentTypes: [.pdf, .plainText]
)
picker.allowsMultipleSelection = true
picker.delegate = self
present(picker, animated: true)
// Giải phóng quyền truy cập trong đại biểu
func documentPicker(
_ controller: UIDocumentPickerViewController,
didPickDocumentsAt urls: [URL]
) {
guard let url = urls.first else { return }
url.startAccessingSecurityScopedResource()
defer { url.stopAccessingSecurityScopedResource() }
}
FileProvider là lớp con của ContentProvider để chia sẻ tệp an toàn giữa các ứng dụng. Nó tạo Uri content:// tạm thời dựa trên tệp từ các thư mục XML được chỉ định. Các ứng dụng khác nhận quyền truy cập qua Intent với FLAG_GRANT_READ_URI_PERMISSION. DocumentProvider, không giống như FileProvider, xuất bản tệp trong SAF và cho phép ứng dụng khác duyệt nội dung của ứng dụng bạn như một phần của hệ thống tệp. Để triển khai DocumentsProvider, bạn cần ghi đè queryRoots(), queryChildDocuments() và openDocument(), sau đó đăng ký trong AndroidManifest.xml
Đồng bộ đám mây cho phép người dùng truy cập tài liệu trên tất cả thiết bị của họ. Hệ thống tệp trong ứng dụng di động được tăng cường bởi lớp đám mây: UIDocument trên iOS tự động theo dõi thay đổi và đồng bộ qua iCloud. Trên Android, chức năng tương tự được xây dựng qua Google Drive API hoặc DocumentsProvider với gốc đám mây. Hiểu hệ thống tệp trong phát triển di động là rất quan trọng để xây dựng đồng bộ đáng tin cậy giữa các thiết bị.
UIDocument là lớp trừu tượng để làm việc với tài liệu iCloud. Nó tự động lưu thay đổi, đọc dữ liệu và thông báo cho đại biểu về cập nhật. Khi xảy ra xung đột ghi, NSFileVersion cung cấp danh sách các phiên bản có sẵn — nhà phát triển có thể chọn phiên bản mới nhất hoặc hiển thị cho người dùng các tùy chọn giải quyết xung đột. Cấu hình Ubiquity Container trong Capabilities của dự án là bắt buộc cho iCloud Drive. NSFileCoordinator và NSFilePresenter ngăn chặn điều kiện cạnh tranh dữ liệu khi truy cập đồng thời từ nhiều luồng hoặc thiết bị.
iOS tự động bao gồm thư mục Documents trong iCloud Backup. Android hoạt động với Auto Backup for Apps — hệ thống lưu dữ liệu từ getFilesDir(), SharedPreferences và cơ sở dữ liệu SQLite vào Google Drive. Bộ nhớ đệm và tệp ngoài không được bao gồm trong sao lưu. Cả hai nền tảng đều cho phép cấu hình ngoại lệ: trên iOS qua NSURLIsExcludedFromBackupKey, trên Android qua cấu hình XML quy tắc sao lưu. Mã hóa tệp có dữ liệu cá nhân là bắt buộc — trên iOS sử dụng NSDataWritingFileProtectionComplete, trên Android sử dụng EncryptedFile từ thư viện security-crypto.
| Tham số | iOS | Android |
|---|---|---|
| Sao lưu mặc định | Documents và Library | getFilesDir(), SharedPreferences, DB |
| Loại trừ tệp | isExcludedFromBackupKey | Quy tắc XML sao lưu (fullBackupContent) |
| Mã hóa | NSDataWritingFileProtectionComplete | EncryptedFile (security-crypto) |
| Đồng bộ đám mây | UIDocument + iCloud | Google Drive API + SAF |
| Tự động khôi phục | iCloud Restore sau khi cài đặt | Auto Backup khi cài lại |
Câu hỏi thường gặp
Sandbox là môi trường cô lập cho mỗi ứng dụng trên iOS. Ứng dụng không thể truy cập tệp của ứng dụng khác nếu không sử dụng bộ chọn hệ thống như UIDocumentPickerViewController.
Scoped Storage là mô hình truy cập hạn chế vào hệ thống tệp trên Android 10+. Ứng dụng chỉ đọc trực tiếp tệp của chính nó, sử dụng MediaStore API cho phương tiện và Storage Access Framework cho tài liệu.
Sử dụng UIDocumentPickerViewController — bộ chọn hệ thống để chọn tài liệu từ Files App hoặc iCloud Drive. Sau khi chọn, bạn nhận được URL có phạm vi bảo mật với quyền truy cập tạm thời.
Đối với tệp phương tiện, sử dụng MediaStore API qua ContentResolver với loại MIME được chỉ định. Đối với tài liệu tùy ý, sử dụng Storage Access Framework với Intent ACTION_OPEN_DOCUMENT.
FileProvider là lớp con của ContentProvider để chia sẻ tệp an toàn giữa các ứng dụng qua Uri content:// tạm thời với FLAG_GRANT_READ_URI_PERMISSION.
Tổng kết
Chúng tôi sẽ phát triển ứng dụng di động chìa khóa trao tay
IT Sectr tạo các ứng dụng iOS và Android cho các công ty khởi nghiệp và doanh nghiệp từ năm 2017. Chúng tôi sẽ tư vấn và đề xuất giải pháp tốt nhất cho bạn.