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(). Для доступу до зовнішньої (спільної) приватної директорії використовується 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 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.

Обговорити проект

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