Scoped Storage — что это, управление файлами и доступ к хранилищу

Автор: IT Sectr Опубликовано: 2026-07-10 Время чтения: 10 мин

Scoped Storage — это модель доступа к файловой системе, введённая в Android 10 (API 29), которая ограничивает произвольный доступ приложений к общему хранилищу устройства. По данным Google Android Developer Documentation (2024), Scoped Storage заменяет старую модель разрешений READ_EXTERNAL_STORAGE и WRITE_EXTERNAL_STORAGE, предоставляя приложениям доступ только к их собственным файлам в изолированной песочнице и к общим коллекциям через MediaStore. Эта модель повышает конфиденциальность пользовательских данных и предотвращает несанкционированный сбор информации приложениями.

Главное

  • Scoped Storage — модель доступа к файлам в Android 10+, ограничивающая произвольное чтение общего хранилища.
  • Приложение имеет собственную песочницу с полным доступом без дополнительных разрешений.
  • Для доступа к общим файлам (фото, видео, аудио) используется MediaStore API.
  • Для доступа к произвольным директориям и файлам используется Storage Access Framework (SAF).
  • Разрешения READ_EXTERNAL_STORAGE и WRITE_EXTERNAL_STORAGE потеряли силу для большинства сценариев.

Что такое Scoped Storage в Android?

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

До Scoped Storage любое приложение с разрешением READ_EXTERNAL_STORAGE могло читать все файлы в общем хранилище — фотографии, документы, загрузки, данные других приложений. Это создавало риски конфиденциальности: недобросовестные приложения могли собирать информацию о пользователе без его ведома.

По данным Google I/O 2019, более 65% пользователей Android считают конфиденциальность данных критическим фактором при выборе приложения. Scoped Storage — прямой ответ на этот запрос: каждое приложение работает в изолированной среде и получает доступ к чужим данным только при явном согласии пользователя.

Важно: Scoped Storage в Android 10 был опциональным (приложение могло отключить через requestLegacyExternalStorage). Начиная с Android 11 (API 30), Scoped Storage стал обязательным для всех приложений, независимо от target SDK.

Зачем понадобился Scoped Storage

Google ввёл Scoped Storage для решения трёх фундаментальных проблем старой модели доступа к файлам: конфиденциальность, управление разрешениями и очистка остаточных файлов.

Проблема конфиденциальности

В старой модели приложение с READ_EXTERNAL_STORAGE могло сканировать всё общее хранилище и собирать метаданные о файлах пользователя — географические координаты фотографий, имена документов, структуру директорий. Scoped Storage устраняет эту возможность: даже имея разрешение, приложение видит только файлы, созданные им самим, и явно выбранные пользователем через SAF.

Проблема очистки при удалении приложения

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

АспектСтарая модель (Legacy)Scoped Storage
Доступ к общему хранилищуПолный (с разрешением)Только своя песочница
МедиафайлыПрямой file pathЧерез MediaStore URI
Чужие файлыДоступны всегдаТолько через SAF
Очистка при удаленииФайлы остаютсяПесочница удаляется
Контроль пользователяМинимальныйЯвное согласие на каждый файл

Scoped Storage — это часть общей стратегии Google по усилению конфиденциальности в Android, которая также включает разрешения "только во время использования", индикаторы доступа к камере и микрофону, и Privacy Dashboard.

Приватная директория приложения

Каждое приложение в Android получает собственную приватную директорию, доступную через Context.getFilesDir() и context.getCacheDir(). Для доступа к внешней (shared) приватной директории используется Context.getExternalFilesDir().

В Scoped Storage приложение имеет полный доступ к своей внешней приватной директории без каких-либо разрешений. Это основное место для хранения файлов, которые не должны быть доступны другим приложениям или которые приложение создаёт для собственного использования.

kotlin
import android.os.Environment
import java.io.File
import java.io.IOException

class FileManager {

    fun saveToAppStorage(context: Context, fileName: String, data: ByteArray) {
        val appDir = context.getExternalFilesDir(null)
            ?: return
        val file = File(appDir, fileName)
        file.writeBytes(data)
    }

    fun readFromAppStorage(context: Context, fileName: String): ByteArray? {
        val appDir = context.getExternalFilesDir(null)
            ?: return null
        val file = File(appDir, fileName)
        return file.takeIf { it.exists() }?.readBytes()
    }
}

Путь к внешней приватной директории: /storage/emulated/0/Android/data/{packageName}/files/. Начиная с Android 11 прямой file path недоступен — только через API. Это ещё одно усиление безопасности Scoped Storage.

MediaStore — доступ к медиафайлам

MediaStore — это основной API для доступа к общим медиафайлам (изображения, видео, аудио) в Scoped Storage. Приложение работает не с file paths, а с content URIs, предоставляемыми MediaStore provider.

MediaStore разделён на три основные коллекции: Images, Video и Audio. Каждая коллекция поддерживает CRUD-операции через ContentResolver. Для вставки нового файла в коллекцию Images используется MediaStore.Images.Media, для запроса существующих — query с соответствующим URI.

kotlin
import android.content.ContentValues
import android.provider.MediaStore
import android.os.Environment
import java.io.OutputStream

fun saveImageToGallery(context: Context, bitmap: Bitmap, title: String) {
    val values = ContentValues().apply {
        put(MediaStore.Images.Media.DISPLAY_NAME, "$title.jpg")
        put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg")
        put(
            MediaStore.Images.Media.RELATIVE_PATH,
            "${Environment.DIRECTORY_PICTURES}/MyApp"
        )
    }

    val uri = context.contentResolver
        .insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values)
        ?: return

    context.contentResolver.openOutputStream(uri)?.use { output: OutputStream ->
        bitmap.compress(
            Bitmap.CompressFormat.JPEG,
            95,
            output
        )
    }
}

Важно: MediaStore не поддерживает произвольные file paths — только content URIs. Попытка получить file path из URI через MediaStore.Files.getContentUri() не даст прямой путь к файлу на Android 11+. Вместо этого используй ContentResolver.openInputStream() и openOutputStream() для работы с содержимым.

Storage Access Framework (SAF)

Storage Access Framework (SAF) — это API для доступа к произвольным файлам и директориям за пределами песочницы приложения. SAF предоставляет пользователю интерфейс выбора файла или директории, после чего приложение получает content URI с временным доступом.

SAF используется для сценариев, которые не покрываются MediaStore: работа с произвольными документами (PDF, ZIP, APK), доступ к директориям на SD-карте, импорт и экспорт файлов из других приложений. Пользователь явно выбирает файл через системный файловый пикер — это гарантирует его согласие на доступ.

kotlin
import android.app.Activity
import android.content.Intent
import android.net.Uri
import android.provider.DocumentsContract

const val REQUEST_CODE_PICK_DIR = 1001

fun pickDirectory(activity: Activity) {
    val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)
    activity.startActivityForResult(intent, REQUEST_CODE_PICK_DIR)
}

fun handlePickResult(requestCode: Int, data: Intent?) {
    if (requestCode == REQUEST_CODE_PICK_DIR && data != null) {
        val treeUri: Uri = data.data ?: return

        // Take persistent URI permission
        val flags = Intent.FLAG_GRANT_READ_URI_PERMISSION or
                       Intent.FLAG_GRANT_WRITE_URI_PERMISSION
        contentResolver.takePersistableUriPermission(treeUri, flags)
    }
}

SAF даёт приложению доступ к выбранной пользователем директории до перезагрузки устройства (при использовании takePersistableUriPermission). Это единственный способ получить доступ к произвольным файлам в общем хранилище на Android 11+.

Миграция приложения на Scoped Storage

Миграция существующего Android-приложения на Scoped Storage требует изменений в нескольких ключевых областях. Google рекомендует поэтапный подход с тестированием на Android 11+.

Замена file paths на content URIs

Замени все прямые File операции в общем хранилище на ContentResolver.openInputStream / openOutputStream. Для файлов в песочнице приложения (getExternalFilesDir) file paths продолжают работать.

Обновление AndroidManifest

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

СценарийСтарый подходScoped Storage подход
Сохранение фотоFile(path).writeBytes()MediaStore + ContentResolver
Чтение PDFFile(path).inputStream()SAF ACTION_OPEN_DOCUMENT
Свои файлыEnvironment.getExternalStorageDirectory()context.getExternalFilesDir()
КэшFile(cacheDir).writeBytes()context.cacheDir (без изменений)

После миграции протестируй приложение на Android 11+ (API 30), включив в build.gradle targetSdk = 30 или выше. Убедись, что все операции с файлами в общем хранилище работают через MediaStore или SAF, а не через прямые file paths.

kotlin
import android.os.Build

fun isScopedStorage(): Boolean {
    return Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q
}

fun compatReadFile(context: Context, uri: Uri): ByteArray? {
    return if (isScopedStorage()) {
        context.contentResolver
            .openInputStream(uri)?.readBytes()
    } else {
        File(uri.path ?: return null)
            .takeIf { it.exists() }
            ?.readBytes()
    }
}

По данным Google Play Console (2024), более 78% активных Android-устройств работают на Android 10+ с Scoped Storage. Миграция — обязательный шаг для публикации обновлений в Google Play: новые приложения должны target API 31+, обновления — API 30+.

Часто задаваемые вопросы

Можно ли отключить Scoped Storage в Android 11?

Нет, начиная с Android 11 (API 30) Scoped Storage обязателен для всех приложений. Флаг requestLegacyExternalStorage, доступный в Android 10, не работает на API 30+. Единственный способ получить широкий доступ к файловой системе — разрешение MANAGE_EXTERNAL_STORAGE, но оно предназначено только для файловых менеджеров и антивирусов.

Что такое MANAGE_EXTERNAL_STORAGE и как его получить?

MANAGE_EXTERNAL_STORAGE — специальное разрешение для приложений, которым нужен полный доступ к файловой системе (файловые менеджеры, backup-инструменты). Запрос разрешения открывает системный экран с предупреждением для пользователя. При публикации в Google Play требуется заполнить форму Declaration о необходимости этого разрешения.

Как получить file path из content URI в Scoped Storage?

В Scoped Storage на Android 11+ прямой file path из content URI получить нельзя. Вместо этого используй ContentResolver.openInputStream() для чтения и openOutputStream() для записи. Если тебе нужен file path для совместимости со сторонней библиотекой — создай копию файла в getCacheDir() и работай с копией.

Как Scoped Storage влияет на FileProvider?

FileProvider продолжает работать без изменений — он используется для предоставления доступа к файлам из песочницы приложения другим приложениям через content URIs. Scoped Storage не влияет на FileProvider, так как FileProvider работает на уровне content URIs, а не прямых file paths.

Как проверить, поддерживает ли устройство Scoped Storage?

Проверь Build.VERSION.SDK_INT: если >= Build.VERSION_CODES.Q (29), то устройство поддерживает Scoped Storage. Однако на Android 10 Scoped Storage может быть отключён через requestLegacyExternalStorage. На Android 11+ проверь, установлено ли флаг-манифест requestLegacyExternalStorage — если нет, Scoped Storage активен.

Итоги

  • Scoped Storage — модель доступа к файлам в Android 10+, ограничивающая произвольное чтение общего хранилища.
  • Приложение имеет полный доступ к своей приватной песочнице без дополнительных разрешений.
  • Для доступа к общим медиафайлам используется MediaStore API с content URIs.
  • Для доступа к произвольным файлам и директориям используется Storage Access Framework (SAF).
  • Начиная с Android 11 Scoped Storage обязателен для всех приложений.
  • Разрешения READ_EXTERNAL_STORAGE и WRITE_EXTERNAL_STORAGE не работают в большинстве сценариев на Android 11+.
  • При миграции замени прямые file paths на ContentResolver.openInputStream / openOutputStream.

Мы разработаем мобильное приложение под ключ

IT Sectr создаёт приложения для iOS и Android для стартапов и бизнеса с 2017 года. Мы проконсультируем вас и предложим наилучшее решение.

Обсудить проект

Читайте также