Storage Access Framework (SAF) — это системный механизм Android, который позволяет приложению открыть документ через стандартный системный диалог выбора файла, не запрашивая широкие разрешения на чтение всего хранилища. Вместо пути к файлу приложение получает URI документа и ограниченные права на него, что соответствует модели конфиденциальности, введённой начиная с Android 10 (scoped storage).
Если приложение просит «выбрать файл» и открывается системное окно с разделами «Загрузки», «Изображения», «Видео» и облачными сервисами — вы как раз видите SAF в действии. Понимание этого механизма полезно и пользователям, которые сталкиваются с ошибкой «Нет доступа к файлу», и разработчикам, мигрирующим приложения на современные версии Android.
Зачем Google ввёл Storage Access Framework
До появления ограничений хранилища любое приложение с разрешением READ_EXTERNAL_STORAGE могло читать все файлы на устройстве — включая личные фото, документы и загрузки. Это создавало очевидные риски приватности: фонарик или калькулятор формально получали доступ ко всей медиатеке.
SAF решает проблему иначе: право доступа привязывается к конкретному документу, который пользователь выбрал сам. Приложение не видит содержимое хранилища целиком — только те файлы и папки, к которым ему явно разрешили доступ. Дополнительный плюс — единый интерфейс для локальных файлов и облачных хранилищ: через один и тот же диалог можно открыть документ из внутренней памяти, с SD-карты или из подключённого облачного провайдера.
Как устроен механизм: документы, провайдеры и URI
В основе SAF лежат три сущности. DocumentsProvider — это поставщик документов: системный (локальное хранилище, внешние накопители) или сторонний (облачные сервисы, если приложение такого сервиса его реализует). Клиентское приложение отправляет intent и получает результат. Системный picker — окно выбора, которое Android отображает поверх приложения и которым управляет сама система, а не приложение.
Результатом выбора является content URI вида content://..., а не файловый путь. Это принципиально: URI остаётся стабильной ссылкой на документ, а доступ к нему контролируется системой. Приложение работает с файлом через ContentResolver — открывает поток чтения или записи по этому URI.
- 📄 Документ — файл с MIME-типом, именем и метаданными, доступный через провайдера
- 📁 Дерево документов — папка, к которой можно получить постоянный доступ целиком
- 🔗 URI permission — выданное системой право на чтение или запись конкретного URI
- ☁️ Провайдер — источник документов: локальный, съёмный накопитель или облако
Основные действия: открытие, создание и доступ к папкам
SAF предоставляет несколько стандартных операций. Выбор конкретной зависит от задачи: нужно ли открыть один файл, создать новый документ или работать с целой папкой на постоянной основе.
| Действие | Intent | Типичный сценарий |
|---|---|---|
| Открыть файл | ACTION_OPEN_DOCUMENT | Пользователь выбирает документ для редактирования |
| Создать файл | ACTION_CREATE_DOCUMENT | Экспорт результата, сохранение копии |
| Доступ к папке | ACTION_OPEN_DOCUMENT_TREE | Приложение работает с каталогом постоянно |
| Быстрый выбор | ACTION_GET_CONTENT | Одноразовое чтение без постоянных прав |
Разница между ACTION_OPEN_DOCUMENT и ACTION_GET_CONTENT принципиальна: первый даёт долгосрочный доступ к документу, который можно сохранить, а второй предназначен для разового чтения — права теряются, как только приложение закрывается или устройство перезагружается.
Постоянные права: persistable URI permission
По умолчанию выданное через picker разрешение временное. Если приложению нужно открывать тот же файл или папку после перезапуска, необходимо закрепить права вызовом takePersistableUriPermission(). Тогда система сохранит разрешение, пока пользователь его не отзовёт или пока не будут исчерпаны системные лимиты на количество сохранённых прав.
⚠️ Внимание: если приложение не вызывает takePersistableUriPermission, после перезагрузки устройства или долгого простоя оно может потерять доступ к ранее выбранному файлу. Симптом со стороны пользователя — ошибка открытия документа или пустой список «недавних файлов» в приложении.
Отозвать выданные разрешения можно и в обратную сторону — методом releasePersistableUriPermission(). Это полезно, когда приложение больше не работает с конкретной папкой: освобождённые права не расходуют лимит и не оставляют «висячих» доступов.
Техническая деталь
флаги прав:При получении URI через onActivityResult (или ActivityResult API) флаги чтения и записи извлекаются из result.getFlags() с маской FLAG_GRANT_READ_URI_PERMISSION и FLAG_GRANT_WRITE_URI_PERMISSION. Именно эти флаги передаются в takePersistableUriPermission — запросить больше, чем выдано, нельзя.
SAF со стороны пользователя: типичные проблемы
Обычный пользователь сталкивается с SAF, когда приложение сообщает, что не может открыть файл, или когда ранее выбранная папка «сбрасывается». Причины чаще всего такие:
- 🔒 Приложение не закрепило постоянные права — доступ пропал после перезагрузки
- 💾 Файл был перемещён или удалён — URI указывает на несуществующий документ
- 📱 SD-карта извлечена или отформатирована — провайдер внешнего накопителя недоступен
- 🗂️ Пользователь отозвал доступ к папке в настройках приложения
Что можно сделать безопасно: повторно выбрать файл или папку через диалог приложения, проверить, что накопитель подключён и виден в системном файловом менеджере, а также убедиться, что документ не был переименован. Если приложение систематически теряет доступ к одной и той же папке, стоит проверить наличие обновлений — в старых версиях некоторых приложений работа с persistable permissions реализована неполностью.
Ограничения и когда SAF не подходит
SAF — не универсальная замена файловому API. Производительность при массовом обходе тысяч файлов через DocumentsContract заметно ниже, чем при прямом доступе, потому что каждая операция идёт через провайдера. Для задач вроде индексации большой медиатеки есть специализированные API (например, MediaStore для медиафайлов).
⚠️ Внимание: приложения категории «файловый менеджер» или антивирусы, которым объективно нужен доступ ко всему хранилищу, используют отдельное разрешение MANAGE_EXTERNAL_STORAGE. Его выдача регулируется политикой Google Play и требует обоснования — это осознанное исключение, а не обход SAF.
Также стоит помнить: SAF не даёт доступа к защищённым системным каталогам (Android/data других приложений и подобным). Это ограничение на уровне платформы, и обходить его нештатными средствами не следует.
Чек-лист для разработчика
Минимальный корректный сценарий работы с SAF выглядит так: запустить picker с нужным intent, получить URI в результате, при необходимости закрепить права и работать с файлом через ContentResolver. Ниже — контрольный список для самопроверки.
☑️ Проверка интеграции SAF
Тестировать стоит на реальных сценариях: перезагрузка устройства, извлечение SD-карты, отзыв разрешения в настройках, удаление файла сторонним менеджером. Именно на этих кейсах всплывают ошибки реализации.
Частые вопросы
Чем SAF отличается от разрешения «Доступ к файлам и медиа»?
Разрешение на хранилище даёт приложению широкий доступ к определённым категориям файлов, а SAF выдаёт права на конкретные документы и папки, которые пользователь выбрал в системном диалоге. SAF не требует запрашивать разрешение на всё хранилище.
Почему приложение каждый раз просит выбрать папку заново?
Наиболее вероятная причина — приложение не закрепило постоянные права через takePersistableUriPermission, поэтому временный доступ сбрасывается. Со стороны пользователя это решается обновлением приложения или выбором альтернативы.
Можно ли через SAF получить доступ к SD-карте?
Да. Через ACTION_OPEN_DOCUMENT_TREE пользователь может выбрать папку или корень съёмного накопителя, и приложение получит доступ к нему через соответствующего провайдера. Набор доступных провайдеров зависит от устройства и версии Android.
Работает ли SAF с облачными хранилищами?
Да, если облачный сервис реализовал DocumentsProvider. Тогда его файлы появляются в системном диалоге выбора наравне с локальными. Конкретный список таких сервисов зависит от установленных на устройстве приложений.
Почему копирование большого числа файлов через SAF медленное?
Каждая операция проходит через провайдера документов и ContentResolver, что добавляет накладные расходы по сравнению с прямым файловым доступом. Для массовых операций с медиа лучше подходит MediaStore API.