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
Читање PDF-аFile(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-а, а не преко директних путања датотека.

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. године. Саветоваћемо вас и предложити најбоље решење.

Разговарајте о пројекту

Прочитајте такође