Scoped Storage — cos'è, gestione dei file e accesso all'archiviazione

Autore: IT Sectr Pubblicato: 2026-07-10 Tempo di lettura: 10 min

Scoped Storage è un modello di accesso al file system introdotto in Android 10 (API 29) che limita l'accesso arbitrario delle app all'archiviazione condivisa del dispositivo. Secondo Google Android Developer Documentation (2024), Scoped Storage sostituisce il vecchio modello di autorizzazioni READ_EXTERNAL_STORAGE e WRITE_EXTERNAL_STORAGE, concedendo alle app l'accesso solo ai propri file in una sandbox isolata e alle raccolte condivise tramite MediaStore. Questo modello migliora la privacy dei dati dell'utente e previene la raccolta non autorizzata di informazioni da parte delle app.

Punti chiave

  • Scoped Storage è un modello di accesso ai file in Android 10+ che limita la lettura arbitraria dell'archiviazione condivisa.
  • L'app ha la propria sandbox con accesso completo senza autorizzazioni aggiuntive.
  • Per accedere ai file condivisi (foto, video, audio) si usa l'API MediaStore.
  • Per accedere a directory e file arbitrari si usa Storage Access Framework (SAF).
  • Le autorizzazioni READ_EXTERNAL_STORAGE e WRITE_EXTERNAL_STORAGE hanno perso efficacia per la maggior parte degli scenari.

Cos'è Scoped Storage in Android?

Scoped Storage è un cambiamento architetturale in Android 10 (API 29) che trasforma fondamentalmente il modo in cui le app accedono al file system del dispositivo. Invece dell'accesso completo all'archiviazione condivisa, l'app ottiene accesso solo alla propria sandbox e a raccolte multimediali specifiche tramite MediaStore.

Prima di Scoped Storage, qualsiasi app con autorizzazione READ_EXTERNAL_STORAGE poteva leggere tutti i file nell'archiviazione condivisa — foto, documenti, download, dati di altre app. Questo creava rischi per la privacy: app inaffidabili potevano raccogliere informazioni sull'utente a sua insaputa.

Secondo Google I/O 2019, oltre il 65% degli utenti Android considera la privacy dei dati un fattore critico nella scelta di un'app. Scoped Storage è una risposta diretta a questa richiesta: ogni app opera in un ambiente isolato e ottiene accesso ai dati di altre app solo con il consenso esplicito dell'utente.

Importante: Scoped Storage in Android 10 era opzionale (l'app poteva disattivarlo tramite requestLegacyExternalStorage). A partire da Android 11 (API 30), Scoped Storage è diventato obbligatorio per tutte le app, indipendentemente dal target SDK.

Perché è stato necessario Scoped Storage

Google ha introdotto Scoped Storage per risolvere tre problemi fondamentali del vecchio modello di accesso ai file: privacy, gestione delle autorizzazioni e pulizia dei file residui.

Problema di privacy

Nel vecchio modello, un'app con READ_EXTERNAL_STORAGE poteva scansionare l'intera archiviazione condivisa e raccogliere metadati dei file dell'utente — coordinate geografiche delle foto, nomi dei documenti, struttura delle directory. Scoped Storage elimina questa possibilità: anche con l'autorizzazione, l'app vede solo i file che ha creato lei stessa e i file selezionati esplicitamente dall'utente tramite SAF.

Problema di pulizia alla disinstallazione dell'app

Prima di Scoped Storage, le app potevano lasciare file nell'archiviazione condivisa dopo la disinstallazione. Con il tempo, si accumulavano directory spazzatura. Scoped Storage risolve questo problema: tutti i file nella sandbox dell'app vengono eliminati insieme all'app, e i file tramite MediaStore hanno un proprietario e possono essere puliti dal sistema.

AspettoVecchio modello (Legacy)Scoped Storage
Accesso all'archiviazione condivisaCompleto (con autorizzazione)Solo la propria sandbox
File multimedialiPercorso file direttoTramite URI MediaStore
File di altre appSempre accessibiliSolo tramite SAF
Pulizia alla disinstallazioneI file rimangonoLa sandbox viene rimossa
Controllo dell'utenteMinimoConsenso esplicito per file

Scoped Storage fa parte della strategia complessiva di privacy di Google in Android, che include anche autorizzazioni "solo durante l'uso", indicatori di accesso a fotocamera e microfono, e il Privacy Dashboard.

Directory privata dell'app

Ogni app Android riceve la propria directory privata, accessibile tramite Context.getFilesDir() e context.getCacheDir(). Per accedere alla directory privata esterna (condivisa), si usa Context.getExternalFilesDir().

In Scoped Storage, un'app ha accesso completo alla sua directory privata esterna senza alcuna autorizzazione. Questo è il luogo principale per archiviare file che non devono essere accessibili ad altre app o che l'app crea per uso proprio.

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()
    }
}

Percorso della directory privata esterna: /storage/emulated/0/Android/data/{packageName}/files/. A partire da Android 11, il percorso file diretto non è disponibile — solo tramite API. Questo è un ulteriore miglioramento della sicurezza di Scoped Storage.

MediaStore — accesso ai file multimediali

MediaStore è l'API principale per accedere ai file multimediali condivisi (immagini, video, audio) in Scoped Storage. L'app non lavora con percorsi file, ma con content URI forniti dal provider MediaStore.

MediaStore è diviso in tre raccolte principali: Images, Video e Audio. Ogni raccolta supporta operazioni CRUD tramite ContentResolver. Per inserire un nuovo file nella raccolta Images, si usa MediaStore.Images.Media; per interrogare i file esistenti — query con l'URI corrispondente.

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
        )
    }
}

Importante: MediaStore non supporta percorsi file arbitrari — solo content URI. Tentare di ottenere un percorso file da un URI tramite MediaStore.Files.getContentUri() non darà un percorso diretto al file su Android 11+. Invece, usa ContentResolver.openInputStream() e openOutputStream() per lavorare con il contenuto.

Storage Access Framework (SAF)

Storage Access Framework (SAF) è un'API per accedere a file e directory arbitrari al di fuori della sandbox dell'app. SAF fornisce all'utente un'interfaccia di selezione di file o directory, dopo di che l'app riceve un content URI con accesso temporaneo.

SAF viene utilizzato per scenari non coperti da MediaStore: lavorare con documenti arbitrari (PDF, ZIP, APK), accedere a directory sulla scheda SD, importare ed esportare file da altre app. L'utente seleziona esplicitamente un file tramite il selettore di file di sistema — questo garantisce il suo consenso all'accesso.

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 concede all'app l'accesso alla directory selezionata dall'utente fino al riavvio del dispositivo (quando si usa takePersistableUriPermission). Questo è l'unico modo per ottenere accesso a file arbitrari nell'archiviazione condivisa su Android 11+.

Migrazione dell'app a Scoped Storage

Migrare un'app Android esistente a Scoped Storage richiede modifiche in diverse aree chiave. Google raccomanda un approccio graduale con test su Android 11+.

Sostituire i percorsi file con content URI

Sostituisci tutte le operazioni dirette con File nell'archiviazione condivisa con ContentResolver.openInputStream / openOutputStream. Per i file nella sandbox dell'app (getExternalFilesDir), i percorsi file continuano a funzionare.

Aggiornare AndroidManifest

Rimuovi READ_EXTERNAL_STORAGE e WRITE_EXTERNAL_STORAGE se non sono necessari per scenari specifici (ad esempio, gestione file — per questo esiste l'autorizzazione speciale MANAGE_EXTERNAL_STORAGE).

ScenarioVecchio approccioApproccio Scoped Storage
Salvare una fotoFile(path).writeBytes()MediaStore + ContentResolver
Leggere PDFFile(path).inputStream()SAF ACTION_OPEN_DOCUMENT
File propriEnvironment.getExternalStorageDirectory()context.getExternalFilesDir()
CacheFile(cacheDir).writeBytes()context.cacheDir (invariato)

Dopo la migrazione, testa l'app su Android 11+ (API 30), impostando targetSdk = 30 o superiore in build.gradle. Assicurati che tutte le operazioni sui file nell'archiviazione condivisa funzionino tramite MediaStore o SAF, non tramite percorsi file diretti.

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()
    }
}

Secondo Google Play Console (2024), oltre il 78% dei dispositivi Android attivi esegue Android 10+ con Scoped Storage. La migrazione è un passo obbligatorio per pubblicare aggiornamenti su Google Play: le nuove app devono avere target API 31+, gli aggiornamenti — API 30+.

Domande frequenti

Si può disabilitare Scoped Storage in Android 11?

No, a partire da Android 11 (API 30) Scoped Storage è obbligatorio per tutte le app. Il flag requestLegacyExternalStorage, disponibile in Android 10, non funziona su API 30+. L'unico modo per ottenere accesso esteso al file system è l'autorizzazione MANAGE_EXTERNAL_STORAGE, ma è destinata solo a file manager e antivirus.

Cos'è MANAGE_EXTERNAL_STORAGE e come si ottiene?

MANAGE_EXTERNAL_STORAGE è un'autorizzazione speciale per app che necessitano di accesso completo al file system (file manager, strumenti di backup). La richiesta di autorizzazione apre una schermata di sistema con un avviso per l'utente. Quando pubblichi su Google Play, devi compilare un modulo di Dichiarazione sulla necessità di questa autorizzazione.

Come ottenere un percorso file da un content URI in Scoped Storage?

In Scoped Storage su Android 11+, non è possibile ottenere un percorso file diretto da un content URI. Invece, usa ContentResolver.openInputStream() per leggere e openOutputStream() per scrivere. Se hai bisogno di un percorso file per compatibilità con una libreria di terze parti, crea una copia del file in getCacheDir() e lavora con la copia.

Come Scoped Storage influisce su FileProvider?

FileProvider continua a funzionare senza modifiche — viene utilizzato per fornire accesso ai file dalla sandbox dell'app ad altre app tramite content URI. Scoped Storage non influisce su FileProvider, poiché FileProvider opera a livello di content URI, non di percorsi file diretti.

Come verificare se un dispositivo supporta Scoped Storage?

Controlla Build.VERSION.SDK_INT: se >= Build.VERSION_CODES.Q (29), il dispositivo supporta Scoped Storage. Tuttavia, su Android 10, Scoped Storage può essere disabilitato tramite requestLegacyExternalStorage. Su Android 11+, verifica se il flag del manifest requestLegacyExternalStorage è impostato — in caso contrario, Scoped Storage è attivo.

Riepilogo

  • Scoped Storage è un modello di accesso ai file in Android 10+ che limita la lettura arbitraria dell'archiviazione condivisa.
  • L'app ha accesso completo alla sua sandbox privata senza autorizzazioni aggiuntive.
  • Per accedere ai file multimediali condivisi, si usa l'API MediaStore con content URI.
  • Per accedere a file e directory arbitrari, si usa Storage Access Framework (SAF).
  • A partire da Android 11, Scoped Storage è obbligatorio per tutte le app.
  • Le autorizzazioni READ_EXTERNAL_STORAGE e WRITE_EXTERNAL_STORAGE non funzionano nella maggior parte degli scenari su Android 11+.
  • Durante la migrazione, sostituisci i percorsi file diretti con ContentResolver.openInputStream / openOutputStream.

Svilupperemo un'applicazione mobile chiavi in mano

IT Sectr crea applicazioni iOS e Android per startup e aziende dal 2017. Ti consulteremo e ti proporremo la soluzione migliore.

Discuti il progetto

Leggi anche