Чтобы предоставить пользователям больше контроля над файлами и уменьшить их количество, в Android 10 представлена новая парадигма хранения для приложений, называемая хранилищем с ограниченной областью действия (Scopeed Storage) . Scopeed Storage меняет способ хранения и доступа приложений к файлам на внешнем хранилище устройства. Чтобы помочь вам перенести приложение для поддержки хранилища с ограниченной областью действия (Scopeed Storage), следуйте рекомендациям для распространённых сценариев использования хранилища, изложенным в этом руководстве. Сценарии использования разделены на две категории: работа с медиафайлами и работа с файлами, не относящимися к медиафайлам .
Во многих случаях ваше приложение создаёт файлы, к которым другим приложениям не нужен или не должен быть доступ. Система предоставляет приложениям специальные хранилища для управления такими файлами.
Дополнительную информацию о хранении и доступе к файлам на устройствах Android см. в обучающих руководствах по хранению данных .
Обработка медиа-файлов
В этом разделе описываются некоторые распространённые варианты использования медиафайлов (видео, изображений и аудиофайлов), а также объясняется высокоуровневый подход, который может использовать ваше приложение. В следующей таблице кратко изложены все эти варианты использования и даны ссылки на разделы с более подробной информацией.
Вариант использования | Краткое содержание |
---|---|
Показать все файлы изображений и видео | Используйте один и тот же подход для всех версий Android. |
Показывать изображения или видео из определенной папки | Используйте один и тот же подход для всех версий Android. |
Доступ к информации о местоположении из фотографий | Используйте один подход, если ваше приложение использует хранилище с ограниченным доступом. Используйте другой подход, если ваше приложение отказывается от хранилища с ограниченным доступом. |
Определить место хранения новых загрузок | Используйте один подход, если ваше приложение использует хранилище с ограниченным доступом. Используйте другой подход, если ваше приложение отказывается от хранилища с ограниченным доступом. |
Экспортировать пользовательские медиафайлы на устройство | Используйте один и тот же подход для всех версий Android. |
Изменение или удаление нескольких медиафайлов за одну операцию | Используйте один подход для Android 11. Для Android 10 откажитесь от использования хранилища с ограниченным доступом и используйте подход для Android 9 и более ранних версий. |
Импортируйте одно уже существующее изображение. | Используйте один и тот же подход для всех версий Android. |
Сделать снимок одного изображения | Используйте один и тот же подход для всех версий Android. |
Делитесь медиафайлами с другими приложениями | Используйте один и тот же подход для всех версий Android. |
Делитесь медиафайлами с помощью определенного приложения | Используйте один и тот же подход для всех версий Android. |
Доступ к файлам из кода или библиотек, использующих прямые пути к файлам | Используйте один подход для Android 11. Для Android 10 откажитесь от использования хранилища с ограниченным доступом и используйте подход для Android 9 и более ранних версий. |
Показывать файлы изображений или видео из нескольких папок
Запросите медиаколлекцию с помощью API query()
. Чтобы отфильтровать или отсортировать медиафайлы, настройте параметры projection
, selection
, selectionArgs
и sortOrder
.
Показывать изображения или видео из определенной папки
Используйте этот подход:
- Следуя рекомендациям, изложенным в разделе Запрос разрешений приложения , запросите разрешение
READ_EXTERNAL_STORAGE
. - Извлечь медиа-файлы на основе значения
MediaColumns.DATA
, которое содержит абсолютный путь в файловой системе к медиа-элементу на диске.
Примечание: При доступе к существующему медиафайлу вы можете использовать значение столбца DATA
в своей логике. Это связано с тем, что это значение содержит допустимый путь к файлу. Однако не стоит полагаться на то, что файл всегда доступен. Будьте готовы к обработке любых возможных ошибок ввода-вывода, связанных с файлами.
С другой стороны, для создания или обновления медиафайла не используйте столбец DATA
. Вместо этого используйте столбцы DISPLAY_NAME
и RELATIVE_PATH
.
Доступ к информации о местоположении из фотографий
Если ваше приложение использует хранилище с ограниченным доступом, следуйте инструкциям в разделе «Информация о местоположении на фотографиях» руководства по хранению мультимедиа.
Определить место хранения новых загрузок
Если ваше приложение использует ограниченное хранилище, будьте внимательны при выборе места для хранения загружаемых вами медиафайлов.
Если другим приложениям требуется доступ к файлам, рассмотрите возможность использования четко определенных коллекций мультимедиа для загрузок или коллекций документов.
На устройствах с Android 11 и более поздних версиях файлы внутри внешнего каталога приложения недоступны для других приложений, даже если вы используете DownloadManager
для загрузки этих файлов.
Экспортировать пользовательские медиафайлы на устройство
Определите правильное место по умолчанию для хранения пользовательских медиафайлов:
- Разрешить пользователям выбирать, делать ли их медиафайлы доступными для чтения другим приложениям или нет, используя хранилище, специфичное для приложения , или общее хранилище .
- Разрешите пользователям экспортировать файлы из каталогов приложения в более общедоступное место. Используйте коллекции изображений, видео и аудио MediaStore для экспорта медиафайлов в галерею устройства.
Изменение или удаление нескольких медиафайлов за одну операцию
Внедрите логику, основанную на версиях Android, на которых работает ваше приложение.
Работает на Android 11
Используйте этот подход:
- Создайте ожидающее намерение для запроса на запись или удаление вашего приложения с помощью
MediaStore.createWriteRequest()
илиMediaStore.createTrashRequest()
, а затем запросите у пользователя разрешение на редактирование набора файлов, вызвав это намерение. Оцените ответ пользователя:
- Если разрешение предоставлено, продолжите операцию изменения или удаления.
- Если разрешение не было предоставлено, объясните пользователю, почему функция вашего приложения требует разрешения.
Узнайте больше о том, как управлять группами медиафайлов с помощью этих методов, доступных в Android 11 и более поздних версиях.
Работает на Android 10
Если ваше приложение предназначено для Android 10 (уровень API 29), откажитесь от использования хранилища с ограниченной областью действия и продолжайте использовать подход для Android 9 и ниже для выполнения этой операции.
Работает на Android 9 или ниже
Используйте этот подход:
- Следуя рекомендациям, изложенным в разделе Запрос разрешений приложения , запросите разрешение
WRITE_EXTERNAL_STORAGE
. - Используйте API
MediaStore
для изменения или удаления медиафайлов.
Импортируйте одно уже существующее изображение.
Если вы хотите импортировать одно уже существующее изображение (например, чтобы использовать его в качестве фотографии для профиля пользователя), ваше приложение может либо использовать собственный пользовательский интерфейс для этой операции, либо использовать системное средство выбора.
Представьте свой собственный пользовательский интерфейс
Используйте этот подход:
- Следуя рекомендациям, изложенным в разделе Запрос разрешений приложения , запросите разрешение
READ_EXTERNAL_STORAGE
. - Используйте API
query()
для запроса медиа-коллекции . - Отобразите результаты в пользовательском интерфейсе вашего приложения.
Используйте системный выбор
Используйте намерение ACTION_GET_CONTENT
, которое предлагает пользователю выбрать изображение для импорта.
Если вы хотите отфильтровать типы изображений, которые системный выбор предоставляет пользователю для выбора, вы можете использовать setType()
или EXTRA_MIME_TYPES
.
Сделать снимок одного изображения
Если вы хотите сделать снимок для использования в приложении (например, в качестве фотографии для профиля пользователя), используйте интент ACTION_IMAGE_CAPTURE
, чтобы попросить пользователя сделать снимок камерой устройства. Система сохранит полученное изображение в таблице MediaStore.Images
.
Делитесь медиафайлами с другими приложениями
Используйте метод insert()
для добавления записей непосредственно в MediaStore. Подробнее см. в разделе «Добавление элемента» руководства по хранилищу мультимедиа.
Делитесь медиафайлами с помощью определенного приложения
Используйте компонент Android FileProvider
, как описано в руководстве по настройке общего доступа к файлам .
Доступ к файлам из кода или библиотек, использующих прямые пути к файлам
Внедрите логику, основанную на версиях Android, на которых работает ваше приложение.
Работает на Android 11
Используйте этот подход:
- Следуя рекомендациям, изложенным в разделе Запрос разрешений приложения , запросите разрешение
READ_EXTERNAL_STORAGE
. - Доступ к файлам осуществляется с использованием прямых путей к файлам.
Более подробную информацию см. в разделе о том, как открывать медиа-файлы с использованием прямых путей к файлам .
Работает на Android 10
Если ваше приложение предназначено для Android 10 (уровень API 29), откажитесь от использования хранилища с ограниченной областью действия и продолжайте использовать подход для Android 9 и ниже для выполнения этой операции.
Работает на Android 9 или ниже
Используйте этот подход:
- Следуя рекомендациям, изложенным в разделе Запрос разрешений приложения , запросите разрешение
WRITE_EXTERNAL_STORAGE
. - Доступ к файлам осуществляется с использованием прямых путей к файлам.
Обработка файлов, не являющихся медиафайлами
В этом разделе описываются некоторые распространённые варианты использования для обработки файлов, не являющихся медиафайлами, и объясняется высокоуровневый подход, который может использовать ваше приложение. В следующей таблице кратко изложены все эти варианты использования и даны ссылки на разделы с более подробной информацией.
Вариант использования | Краткое содержание |
---|---|
Открыть файл документа | Используйте один и тот же подход для всех версий Android. |
Запись в файлы на вторичных томах хранения | Используйте один подход для Android 11. Используйте другой подход для более ранних версий Android. |
Перенести существующие файлы из старого места хранения | По возможности переносите файлы в выделенное хранилище. При необходимости откажитесь от выделенного хранилища для Android 10. |
Делитесь контентом с другими приложениями | Используйте один и тот же подход для всех версий Android. |
Кэшировать немедийные файлы | Используйте один и тот же подход для всех версий Android. |
Экспорт файлов, не являющихся медиафайлами, на устройство | Используйте один подход, если ваше приложение использует хранилище с ограниченным доступом. Используйте другой подход, если ваше приложение отказывается от хранилища с ограниченным доступом. |
Открыть файл документа
Используйте интент ACTION_OPEN_DOCUMENT
, чтобы предложить пользователю выбрать файл для открытия с помощью системного средства выбора. Если вы хотите отфильтровать типы файлов, которые системное средство выбора предложит пользователю, можно использовать setType()
или EXTRA_MIME_TYPES
.
Например, вы можете найти все файлы PDF, ODT и TXT, используя следующий код:
Котлин
startActivityForResult( Intent(Intent.ACTION_OPEN_DOCUMENT).apply { addCategory(Intent.CATEGORY_OPENABLE) type = "*/*" putExtra(Intent.EXTRA_MIME_TYPES, arrayOf( "application/pdf", // .pdf "application/vnd.oasis.opendocument.text", // .odt "text/plain" // .txt )) }, REQUEST_CODE )
Ява
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT); intent.addCategory(Intent.CATEGORY_OPENABLE); intent.setType("*/*"); intent.putExtra(Intent.EXTRA_MIME_TYPES, new String[] { "application/pdf", // .pdf "application/vnd.oasis.opendocument.text", // .odt "text/plain" // .txt }); startActivityForResult(intent, REQUEST_CODE);
Запись в файлы на вторичных томах хранения
К вторичным томам хранения относятся SD-карты. Доступ к информации о томе хранения осуществляется с помощью класса StorageVolume
.
Внедрите логику, основанную на версии Android, на которой работает ваше приложение.
Работает на Android 11
Используйте этот подход:
- Используйте модель хранилища с ограниченной областью действия .
- Целевая версия Android 10 (уровень API 29) или ниже.
- Объявите разрешение
WRITE_EXTERNAL_STORAGE
. - Выполните один из следующих типов доступа:
- Доступ к файлам с использованием API
MediaStore
. - Прямой доступ к файлу с использованием API, таких как
File
илиfopen()
.
- Доступ к файлам с использованием API
Работает на старых версиях
Используйте Storage Access Framework , который позволяет пользователям выбирать место на вторичном томе хранилища, куда ваше приложение может записать файл.
Перенести существующие файлы из старого места хранения
Каталог считается устаревшим хранилищем, если он не является каталогом конкретного приложения или общедоступным общим каталогом. Если ваше приложение создаёт или использует файлы в устаревшем хранилище, мы рекомендуем перенести файлы приложения в расположения, доступные для хранилища с ограниченным доступом, и внести необходимые изменения в приложение для работы с файлами в хранилище с ограниченным доступом.
Сохраняйте доступ к старому месту хранения для миграции данных.
Вашему приложению необходимо сохранить доступ к старому хранилищу, чтобы иметь возможность переносить файлы приложения в расположения, доступные через хранилище с ограниченной областью действия. Выбор подхода зависит от целевого уровня API вашего приложения.
Если ваше приложение предназначено для Android 11
Установите для флага
preserveLegacyExternalStorage
значениеtrue
, чтобы сохранить устаревшую модель хранения и ваше приложение могло перенести данные пользователя при обновлении до новой версии вашего приложения, предназначенной для Android 11.Продолжайте отказываться от использования хранилища с ограниченным доступом , чтобы ваше приложение могло продолжать получать доступ к файлам в прежнем месте хранения на устройствах Android 10.
Если ваше приложение предназначено для Android 10
Откажитесь от использования хранилища с ограниченным доступом , чтобы упростить поддержку поведения вашего приложения в различных версиях Android.
Перенос данных приложения
Когда ваше приложение будет готово к миграции, используйте следующий подход:
- Целевая версия Android 10 или ниже.
- Откажитесь от использования хранилища с ограниченным доступом, чтобы ваше приложение имело доступ к файлам, которые необходимо перенести.
Разверните код, который использует
File
API для перемещения файлов из их текущего местоположения в/sdcard/
в местоположение, доступное с помощью хранилища с ограниченной областью действия:- Переместите все файлы частного приложения в каталог, возвращаемый методом
getExternalFilesDir()
. - Переместите все общие файлы, не являющиеся медиафайлами, в подкаталог, предназначенный для приложения, в каталоге
Downloads/
.
- Переместите все файлы частного приложения в каталог, возвращаемый методом
- Удалите устаревшие каталоги хранения вашего приложения из каталога
/sdcard/
.
После установки новой версии вашего приложения пользователи завершают процесс переноса данных на своих устройствах. Вы можете отслеживать процесс переноса по всей базе пользователей, создав аналитическое событие.
После того как пользователи перенесут свои данные, опубликуйте еще одно обновление вашего приложения, ориентированное на Android 11.
Делитесь контентом с другими приложениями
Чтобы предоставить доступ к файлам вашего приложения только одному другому приложению, используйте FileProvider
. Для приложений, которым необходимо обмениваться файлами, мы рекомендуем использовать поставщика контента для каждого приложения, а затем синхронизировать данные по мере добавления приложений в коллекцию.
Кэшировать немедийные файлы
Подход, который следует использовать, зависит от типа файлов, которые необходимо кэшировать.
- Небольшие файлы или файлы, содержащие конфиденциальную информацию : используйте
Context#getCacheDir()
. - Большие файлы или файлы, не содержащие конфиденциальной информации : используйте
Context#getExternalCacheDir()
.
Экспорт файлов, не являющихся медиафайлами, на устройство
Определите правильное место по умолчанию для хранения файлов, не относящихся к мультимедиа. Разрешите пользователям экспортировать файлы из каталогов приложения в более общедоступное место. Используйте загрузки или коллекции документов MediaStore для экспорта файлов, не относящихся к мультимедиа, на устройство.
Обработка файлов, специфичных для приложения
Если ваше приложение создает файлы, к которым другим приложениям не нужен или не должен быть доступ, вы можете хранить эти файлы в местах хранения, предназначенных для конкретного приложения .
Внутренние каталоги хранения
Система блокирует доступ других приложений к этим местоположениям, а на Android 10 (уровень API 29) и выше эти местоположения зашифрованы. Эти местоположения отлично подходят для хранения конфиденциальных данных, доступ к которым есть только у вашего приложения.
Внешние каталоги хранения
Если внутреннего хранилища недостаточно для хранения файлов приложения, рассмотрите возможность использования внешнего хранилища. Хотя другое приложение может получить доступ к этим каталогам при наличии соответствующих разрешений, файлы, хранящиеся в этих каталогах, предназначены только для использования вашим приложением.
На устройствах с Android 4.4 (уровень API 19) и выше вашему приложению не нужно запрашивать какие-либо разрешения на хранилище для доступа к каталогам приложения во внешнем хранилище.
Когда пользователь удаляет ваше приложение, файлы, сохраненные в хранилище приложения, удаляются, и, следовательно, вам не следует использовать это хранилище для сохранения чего-либо, что пользователь ожидает сохранить независимо от вашего приложения.
Временно отказаться от использования хранилища с ограниченным доступом
До того как ваше приложение станет полностью совместимым с хранилищем scoped, вы можете временно отказаться от него как в своих тестах , так и в своем рабочем приложении .
Откажитесь от участия в тестах
В Android 10 (уровень API 29) и выше тесты вашего приложения по умолчанию выполняются в изолированной среде хранилища. Эта изолированная среда предотвращает доступ вашего приложения к файлам за пределами каталога приложения и общедоступных каталогов.
Если тест выводит файлы для хоста, такие как снимки экрана, данные отладки, данные о покрытии или метрики производительности, вы можете записать эти файлы в глобальные каталоги. Для этого добавьте следующий флаг в соответствующую утилиту, вызывающую am instrument
:
-e no-isolated-storage 1
Этот флаг влияет на всё поведение инструментированного тестового случая и на весь вызываемый тестовый код. Следовательно, при использовании этого флага невозможно проверить совместимость приложения с хранилищем в области действия. Для тестовых данных лучше записывать данные в хранилище в области действия приложения, доступное для чтения оболочкой. Затем можно извлечь данные из этого каталога в области действия приложения. Чтобы определить, из какого каталога следует извлекать данные, вызовите метод getExternalMediaDirs()
.
Откажитесь от участия в вашем производственном приложении
Если ваше приложение предназначено для Android 10 (уровень API 29) или ниже, вы можете временно отказаться от использования хранилища scoped в вашем производственном приложении. Однако, если вы используете Android 10, вам необходимо установить значение requestLegacyExternalStorage
равным true
в файле манифеста вашего приложения:
<manifest ... > <!-- This attribute is "false" by default on apps targeting Android 10. --> <application android:requestLegacyExternalStorage="true" ... > ... </application> </manifest>
Чтобы проверить, как приложение для Android 10 или более ранних версий будет вести себя при использовании хранилища с ограниченной областью действия, вы можете включить это поведение, установив значение requestLegacyExternalStorage
на false
. При тестировании на устройстве под управлением Android 11 вы также можете использовать флаги совместимости приложений, чтобы проверить поведение приложения как с ограниченной областью действия, так и без неё.
Дополнительные ресурсы
Дополнительную информацию о хранилище Android можно найти в следующих материалах: