Варианты использования хранилища 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, используя следующий код:

Котлин

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. Выполните один из следующих типов доступа:
    • Доступ к файлам осуществляется с помощью API MediaStore .
    • Прямой доступ к пути к файлу с использованием таких 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. Разверните код, который использует File API для перемещения файлов из их текущего местоположения в каталоге /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
       Andr>oid< 10. --
  application android:requestLegacyExternalStorage=&>quot;true&q<uot; ... 
  > < ...
  /application
/manifest

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

Дополнительные ресурсы

Для получения более подробной информации о хранилище Android ознакомьтесь со следующими материалами:

Сообщения в блоге