Что такое legacy storage и почему нужно переходить сейчас

Legacy storage — это режим доступа к внешнему хранилищу через android:requestLegacyExternalStorage, который позволял приложению полный доступ к общим папкам. В новых Android (11+) этот режим запрещён для новых приложений: нужно мигрировать на Scoped Storage (MediaStore, Storage Access Framework, Photo Picker).

Что такое legacy storage — коротко и по делу

Legacy storage давал приложениям прямой доступ к /storage/emulated/0 и другим общим папкам без ограничений. Разработчики включали android:requestLegacyExternalStorage="true" в манифесте, чтобы обойти ограничения Android 10+. Это удобно, но нарушало приватность: приложения видели и могли менять чужие файлы, что приводило к уязвимостям.

Почему Google убирает legacy и что это даёт

Scoped Storage (введён в Android 10, обязателен с API 30+) ограничивает доступ: приложение видит только свои файлы или работает через официальные API:

  • MediaStore — для фото, видео, аудио;
  • Storage Access Framework (SAF) — для произвольных файлов и выбора папок;
  • Photo Picker и granular media permissions — более удобные способы работы с медиа в Android 13+.

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

Если ваше приложение targetSdkVersion ≥ 30 и всё ещё полагается на legacy, обновление в Google Play может быть отклонено. Проверьте сборки на эмуляторе/устройстве Android 14.

Изменения по версиям Android (кратко)

ВерсияСтатус legacyЧто нужно делать
до Android 10Полный доступСтарые приложения работали свободно
Android 10 (API 29)Опция через requestLegacyExternalStorageНастройте флаг временно, планируйте миграцию
Android 11 (API 30)Обязателен Scoped для новых targetSdkОбновите код на MediaStore/SAF
Android 12/13+Флаг игнорируется для новых установокИспользуйте Photo Picker, granular perms

Практические шаги миграции на Scoped Storage

  1. Удалите android:requestLegacyExternalStorage из манифеста (или установите false).
  2. Определите сценарии доступа:
    • медиа (фото/видео/аудио) — MediaStore или Photo Picker;
    • произвольные файлы / каталоги — Storage Access Framework (ACTION_OPEN_DOCUMENT_TREE / ACTION_CREATE_DOCUMENT).
  3. Для записи в MediaStore (Kotlin, упрощённо):
val values = ContentValues().apply {
  put(MediaStore.Images.Media.DISPLAY_NAME, "photo.jpg")
  put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg")
  put(MediaStore.Images.Media.RELATIVE_PATH, "DCIM/MyApp")
}
val uri = contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values)
contentResolver.openOutputStream(uri!!).use { out -> out.write(bytes) }
  1. Для произвольных директорий — запрашивайте ACTION_OPEN_DOCUMENT_TREE и сохраняйте доступ:
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)
startActivityForResult(intent, REQ_TREE)
contentResolver.takePersistableUriPermission(uri, flags)
  1. Обновите разрешения: READ/WRITE_EXTERNAL_STORAGE устарели; используйте granular media permissions (READ_MEDIA_IMAGES, READ_MEDIA_VIDEO и т.д.) на Android 13+.
  2. Тестируйте на реальных устройствах (вендоры могут иметь свои особенности).

Для галерей и выбора фото начните с Photo Picker API (Android 13+): он проще, показывает только медиа и не требует хранения разрешений.

Частые ошибки и как их избежать

  • Пытаетесь писать напрямую в /storage/emulated/0/Download — File API заблокирован. Решение: MediaStore или SAF.
  • Запрашиваете старые разрешения на Android 13+ — они игнорируются. Используйте READ_MEDIA_*.
  • Не ловите SecurityException — добавьте fallback на SAF или покажите пользователю понятный диалог.
  • Не сохраняете persistable URI — после перезапуска доступ потеряется; используйте takePersistableUriPermission.

FAQ

  • Нужно ли мигрировать сейчас? Да: для публикации и обновлений в Play Store обновление обязательно при targetSdk ≥ 30.
  • Что быстрее — MediaStore или SAF? Для медиа быстрее MediaStore; для произвольных файлов — SAF даёт контроль и UX выбора.
  • Можно ли полностью эмулировать старое поведение? Нет — только частичные исключения через Play Console для редких legacy-сценариев, но их не дают массово.

Итог: планируйте миграцию по этапам — инвентаризация сценариев, прототип на MediaStore/SAF, тесты на вендорах, затем обновление targetSdk. Это повысит безопасность и стабильность приложения.