Рекомендации по работе с хранилищем Android и примеры его использования

Чтобы предоставить пользователям больше контроля над файлами и ограничить их беспорядочное хранение, в Android 10 была представлена новая парадигма хранения данных для приложений, которая называется областями хранения данных. Области хранения данных меняют способ, которым приложения сохраняют файлы на внешнем накопителе устройства и получают к ним доступ. Чтобы вам было проще перевести приложение на работу с областями хранения данных, мы подготовили рекомендации для распространенных сценариев использования хранилища. Примеры использования разделены на две категории: работа с медиафайлами и работа с другими файлами.

Во многих случаях приложение создает файлы, к которым другим приложениям не нужен или не должен предоставляться доступ. Для управления такими файлами в системе предусмотрены хранилища, предназначенные для отдельных приложений.

Чтобы узнать больше о хранении файлов и доступе к ним на устройствах 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.

Показывать изображения или видео из определенной папки.

Используйте этот подход:

  1. Следуя рекомендациям из статьи Запрос разрешений для приложения, запросите разрешение READ_EXTERNAL_STORAGE.
  2. Получать медиафайлы на основе значения MediaColumns.DATA, которое содержит абсолютный путь к мультимедийному объекту на диске.

Примечание. При доступе к существующему медиафайлу вы можете использовать в своей логике значение столбца DATA. Это связано с тем, что у этого значения есть действительный путь к файлу. Однако не следует полагать, что файл всегда доступен. Будьте готовы к обработке любых ошибок ввода-вывода, связанных с файлами.

Чтобы создать или обновить медиафайл, не используйте столбец DATA. Вместо этого используйте столбцы DISPLAY_NAME и RELATIVE_PATH.

Доступ к геоданным из фотографий

Если в вашем приложении используются области хранения данных, следуйте инструкциям в разделе Геоданные на фотографиях руководства по хранению медиаконтента.

Как выбрать место для скачивания

Если в вашем приложении используются области хранения данных, внимательно выбирайте место для сохранения скачанных медиафайлов.

Если другим приложениям требуется доступ к файлам, рекомендуем использовать четко определенные коллекции медиафайлов для скачивания или коллекции документов.

В Android 11 и более поздних версиях файлы из внешнего каталога приложения недоступны для других приложений, даже если вы используете DownloadManager для их получения.

Как экспортировать медиафайлы пользователя на устройство

Укажите подходящее местоположение по умолчанию для хранения медиафайлов пользователей:

Изменение или удаление нескольких медиафайлов за одну операцию

Включите логику, основанную на версиях Android, на которых работает ваше приложение.

Работает на Android 11

Используйте этот подход:

  1. Создайте отложенный интент для запроса на запись или удаление, используя MediaStore.createWriteRequest() или MediaStore.createTrashRequest(), а затем запросите у пользователя разрешение на изменение набора файлов, вызвав этот интент.
  2. Оцените ответ пользователя:

    • Если разрешение предоставлено, выполните операцию изменения или удаления.
    • Если разрешение не было предоставлено, объясните пользователю, почему оно необходимо для работы функции.

Подробнее о том, как управлять группами медиафайлов с помощью этих методов, доступных в Android 11 и более поздних версиях…

ОС Android 10;

Если ваше приложение предназначено для Android 10 (уровень API 29), откажитесь от использования ограниченного хранилища и продолжайте использовать подход для Android 9 и более ранних версий.

Android 9 или более ранней версии;

Используйте этот подход:

  1. Следуя рекомендациям из статьи Запрос разрешений для приложения, запросите разрешение WRITE_EXTERNAL_STORAGE.
  2. Для этого нужно использовать API MediaStore.

Импорт одного существующего изображения

Если вы хотите импортировать одно изображение, которое уже существует (например, чтобы использовать его в качестве фотографии для профиля пользователя), ваше приложение может использовать для этой операции собственный пользовательский интерфейс или системный инструмент выбора.

Показ собственного пользовательского интерфейса

Используйте этот подход:

  1. Следуя рекомендациям из статьи Запрос разрешений для приложения, запросите разрешение READ_EXTERNAL_STORAGE.
  2. Используйте API query(), чтобы запросить коллекцию медиаконтента.
  3. Покажите результаты в пользовательском интерфейсе приложения.

Используйте системный инструмент выбора

Используйте намерение ACTION_GET_CONTENT, которое предлагает пользователю выбрать изображение для импорта.

Если вы хотите отфильтровать типы изображений, которые системный инструмент выбора предлагает пользователю, используйте setType() или EXTRA_MIME_TYPES.

Сделайте один снимок.

Если вам нужно получить одно изображение для использования в приложении (например, для фотографии профиля пользователя), используйте намерение ACTION_IMAGE_CAPTURE, чтобы попросить пользователя сделать снимок с помощью камеры устройства. Система сохраняет сделанную фотографию в таблице MediaStore.Images.

Как поделиться медиафайлами с другими приложениями

Чтобы добавить записи непосредственно в MediaStore, используйте метод insert(). Подробнее об этом рассказывается в разделе Добавление элемента руководства по хранилищу медиафайлов.

Как поделиться медиафайлами с определенным приложением

Используйте компонент Android FileProvider, как описано в руководстве Настройка общего доступа к файлам.

Доступ к файлам из кода или библиотек, использующих прямые пути к файлам.

Включите логику, основанную на версиях Android, на которых работает ваше приложение.

Работает на Android 11

Используйте этот подход:

  1. Следуя рекомендациям из статьи Запрос разрешений для приложения, запросите разрешение READ_EXTERNAL_STORAGE.
  2. Получите доступ к файлам, используя прямые пути к ним.

Подробнее о том, как открывать медиафайлы с помощью прямых путей к файлам…

ОС Android 10;

Если ваше приложение предназначено для Android 10 (уровень API 29), откажитесь от использования ограниченного хранилища и продолжайте использовать подход для Android 9 и более ранних версий.

Android 9 или более ранней версии;

Используйте этот подход:

  1. Следуя рекомендациям из статьи Запрос разрешений для приложения, запросите разрешение WRITE_EXTERNAL_STORAGE.
  2. Получать доступ к файлам по прямым путям.

Как обрабатывать файлы, не относящиеся к медиаконтенту

В этом разделе описываются некоторые распространенные варианты использования для обработки немедийных файлов. и объясняет высокоуровневый подход, который может использовать ваше приложение. В таблице ниже приведены краткие сведения о каждом из этих способов использования и ссылки на разделы с подробной информацией.

Пример использования Сводка
Как открыть документ Используйте один и тот же подход для всех версий Android.
Запись в файлы на дополнительных носителях Для Android 11 используйте один подход. Для более ранних версий Android используйте другой подход.
Перенос существующих файлов из устаревшего хранилища По возможности перенесите файлы в области хранения данных. При необходимости отключите области хранения данных для Android 10.
Как поделиться контентом с другими приложениями Используйте один и тот же подход для всех версий Android.
Кеширование немедийных файлов Используйте один и тот же подход для всех версий Android.
Как экспортировать файлы, не относящиеся к медиаконтенту, на устройство Если в приложении используются области хранения данных, выберите один подход. Если ваше приложение не использует области хранения данных, выберите другой подход.

Как открыть документ

Используйте намерение ACTION_OPEN_DOCUMENT, чтобы предложить пользователю выбрать файл для открытия с помощью системного инструмента выбора. Если вы хотите отфильтровать типы файлов, которые системный инструмент выбора будет предлагать пользователю, используйте setType() или EXTRA_MIME_TYPES.

Например, вы можете найти все файлы PDF, ODT и TXT, используя следующий код:

Kotlin

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
      )

Java

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

Используйте этот подход:

  1. Используйте модель областей хранения данных.
  2. Приложение предназначено для Android 10 (уровень API 29) или более ранней версии.
  3. Объявите разрешение WRITE_EXTERNAL_STORAGE.
  4. Выполните одно из следующих действий:
    • Доступ к файлу через MediaStore API.
    • Прямой доступ к пути к файлу с помощью API, например File или fopen().

Работа на старых версиях

Используйте Storage Access Framework, чтобы пользователи могли выбрать место на дополнительном накопителе, куда приложение может записать файл.

Перенос существующих файлов из устаревшего хранилища

Каталог считается устаревшим местом хранения, если он не является каталогом приложения или общедоступным каталогом. Если ваше приложение создает или использует файлы в устаревшем хранилище, рекомендуем перенести их в места, доступные при использовании областей хранения данных, и внести в приложение необходимые изменения.

Сохраните доступ к старому хранилищу для переноса данных

Приложению нужен доступ к устаревшему хранилищу, чтобы перенести файлы в области хранения данных. Выбор подхода зависит от целевого уровня API вашего приложения.

Если ваше приложение предназначено для Android 11
  1. Установите для флага preserveLegacyExternalStorage значение true, чтобы сохранить устаревшую модель хранения. Это позволит приложению перенести пользовательские данные при обновлении до новой версии, предназначенной для Android 11.

  2. Продолжайте отказываться от использования областей хранения данных, чтобы ваше приложение могло получать доступ к файлам в прежнем хранилище на устройствах с Android 10.

Если приложение предназначено для Android 10

Отключите области хранения данных, чтобы упростить поддержку работы приложения в разных версиях Android.

Перенос данных приложений

Когда ваше приложение будет готово к переходу, выполните следующие действия:

  1. Целевая версия Android – 10 или более ранняя.
  2. Отключите области хранения данных, чтобы у приложения был доступ к файлам, которые нужно перенести.
  3. Разверните код, который использует API File для перемещения файлов из их текущего местоположения в /sdcard/ в местоположение, доступное с помощью области хранения данных.

    1. Переместите все личные файлы приложения в каталог, возвращаемый методом getExternalFilesDir().
    2. Переместите все общие файлы, не относящиеся к медиаконтенту, в подкаталог каталога Downloads/, предназначенный для приложения.
  4. Удалите устаревшие каталоги хранилища приложения из каталога /sdcard/.

После установки новой версии приложения пользователи завершают процесс переноса данных на своих устройствах. Вы можете отслеживать процесс перехода на новую версию среди пользователей, создав событие аналитики.

После того как пользователи перенесут данные, опубликуйте ещё одно обновление приложения, предназначенное для Android 11.

Как поделиться контентом с другими приложениями

Чтобы поделиться файлами приложения с другим приложением, используйте FileProvider. Если приложениям нужно обмениваться файлами, рекомендуем использовать поставщика контента для каждого приложения, а затем синхронизировать данные по мере добавления приложений в коллекцию.

Кешировать немедийные файлы

Подход, который вы должны использовать, зависит от типа файлов, которые вам нужны. кэшировать.

  • Небольшие файлы или файлы, содержащие конфиденциальную информацию. Используйте Context#getCacheDir().
  • Большие файлы или файлы, не содержащие конфиденциальную информацию. Используйте Context#getExternalCacheDir().

Как экспортировать файлы, не относящиеся к медиаконтенту, на устройство

Укажите правильное местоположение по умолчанию для хранения немедиафайлов. Разрешите пользователям экспортировать файлы из каталогов приложений в более доступное место. Используйте коллекции скачанных файлов или документов MediaStore, чтобы экспортировать на устройство файлы, не относящиеся к медиа.

Работа с файлами приложений

Если ваше приложение создает файлы, к которым другим приложениям не нужен или не должен предоставляться доступ, вы можете хранить эти файлы в хранилищах, предназначенных для определенных приложений.

Каталоги внутреннего хранилища

Система запрещает другим приложениям доступ к этим папкам, а в Android 10 (уровень API 29) и более поздних версиях они зашифрованы. В этих папках удобно хранить конфиденциальные данные, доступ к которым должен быть только у вашего приложения.

Каталоги внешнего хранилища

Если во внутренней памяти недостаточно места для хранения файлов приложения, используйте внешнее хранилище. Хотя другое приложение может получить доступ к этим каталогам, если у него есть соответствующие разрешения, файлы, хранящиеся в этих каталогах, предназначены для использования только вашим приложением.

В Android 4.4 (уровень API 19) и более поздних версиях приложениям не нужно запрашивать разрешения на доступ к хранилищу, чтобы использовать собственные каталоги на внешнем накопителе.

Когда пользователь удаляет приложение, файлы, сохраненные в хранилище приложения, удаляются, поэтому не следует использовать это хранилище для сохранения всего, что пользователь ожидает сохранить независимо от вашего приложения.

Временное отключение областей хранения данных

Пока вы не обеспечите полную совместимость приложения с областями хранения данных, вы можете временно отключить эту функцию как в тестовой, так и в рабочей версии приложения.

Как отказаться от участия в тестировании

На устройствах с Android 10 (уровень API 29) и более поздних версий тесты приложения по умолчанию выполняются в изолированной среде. Эта песочница не позволяет приложению получать доступ к файлам за пределами каталога приложения и общедоступных каталогов.

Если тест выводит файлы для хоста, например скриншоты, данные отладки, данные о покрытии или показатели эффективности, вы можете записать эти файлы в глобальные каталоги. Для этого добавьте следующий флаг в соответствующий набор, который вызывает am instrument:

-e no-isolated-storage 1

Этот флаг влияет на все поведение инструментального тестового примера и на весь вызываемый тестовый код. Поэтому, если вы используете этот флаг, вы не сможете проверить совместимость приложения с областями хранения данных. Для вывода результатов тестирования лучше использовать хранилище, доступное для чтения оболочкой. Затем вы можете извлечь этот каталог, относящийся к приложению. Чтобы определить, из какого каталога извлекать данные, вызовите getExternalMediaDirs().

Как отказаться от участия в программе в рабочей версии приложения

Если ваше приложение предназначено для Android 10 (уровень API 29) или более ранней версии, вы можете временно отказаться от использования областей хранения данных в рабочей версии приложения. Однако если вы используете 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 можно найти в следующих материалах:

Записи в блоге