Что такое 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
- Удалите android:requestLegacyExternalStorage из манифеста (или установите false).
- Определите сценарии доступа:
- медиа (фото/видео/аудио) — MediaStore или Photo Picker;
- произвольные файлы / каталоги — Storage Access Framework (ACTION_OPEN_DOCUMENT_TREE / ACTION_CREATE_DOCUMENT).
- Для записи в 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) }
- Для произвольных директорий — запрашивайте ACTION_OPEN_DOCUMENT_TREE и сохраняйте доступ:
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)
startActivityForResult(intent, REQ_TREE)
contentResolver.takePersistableUriPermission(uri, flags)
- Обновите разрешения: READ/WRITE_EXTERNAL_STORAGE устарели; используйте granular media permissions (READ_MEDIA_IMAGES, READ_MEDIA_VIDEO и т.д.) на Android 13+.
- Тестируйте на реальных устройствах (вендоры могут иметь свои особенности).
Для галерей и выбора фото начните с 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. Это повысит безопасность и стабильность приложения.