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
Достъп до общото хранилищеПълен (с разрешение)Само собствена пясъчна кутия
Медийни файловеДиректен път на файлЧрез 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, директният път на файл не е достъпен — само чрез API. Това е още едно укрепване на сигурността на Scoped Storage.

MediaStore — достъп до медийни файлове

MediaStore — е основният API за достъп до общи медийни файлове (изображения, видео, аудио) в Scoped Storage. Приложението работи не с пътища на файлове, а с 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 не поддържа произволни пътища на файлове — само content URIs. Опитът за получаване на път на файл от 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

        // Вземете постоянно URI разрешение
        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+.

Замена на пътища на файлове с content URIs

Заменете всички директни File операции в общото хранилище с ContentResolver.openInputStream / openOutputStream. За файлове в пясъчната кутия на приложението (getExternalFilesDir) пътищата на файлове продължават да работят.

Актуализиране на 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), като зададете targetSdk = 30 или по-високо в build.gradle. Уверете се, че всички операции с файлове в общото хранилище работят чрез MediaStore или SAF, а не чрез директни пътища на файлове.

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: новите приложения трябва да таргетират 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 за необходимостта от това разрешение.

Как да получите път на файл от content URI в Scoped Storage?

В Scoped Storage на Android 11+ не може да се получи директен път на файл от content URI. Вместо това използвайте ContentResolver.openInputStream() за четене и openOutputStream() за запис. Ако ви е необходим път на файл за съвместимост с библиотека на трета страна — създайте копие на файла в getCacheDir() и работете с копието.

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

FileProvider продължава да работи без промени — използва се за предоставяне на достъп до файлове от пясъчната кутия на приложението на други приложения чрез content URIs. Scoped Storage не влияе на FileProvider, тъй като FileProvider работи на ниво content URIs, а не на директни пътища на файлове.

Как да проверите дали устройството поддържа 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+.
  • При миграция заменете директните пътища на файлове с ContentResolver.openInputStream / openOutputStream.

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

IT Sectr създава iOS и Android приложения за стартъпи и бизнеси от 2017 г. Ще ви консултираме и ще предложим най-доброто решение.

Обсъдете проекта

Прочетете също