Scoped Storage — adalah model akses ke sistem file yang diperkenalkan di Android 10 (API 29) yang membatasi akses sembarang aplikasi ke penyimpanan bersama perangkat. Menurut Google Android Developer Documentation (2024), Scoped Storage menggantikan model izin READ_EXTERNAL_STORAGE dan WRITE_EXTERNAL_STORAGE yang lama, memberikan aplikasi akses hanya ke file mereka sendiri di sandbox terisolasi dan ke koleksi bersama melalui MediaStore. Model ini meningkatkan privasi data pengguna dan mencegah pengumpulan informasi tidak sah oleh aplikasi.
Poin Utama
Scoped Storage — adalah perubahan arsitektur di Android 10 (API 29) yang secara fundamental mengubah cara aplikasi mengakses sistem file perangkat. Alih-alih akses penuh ke penyimpanan bersama, aplikasi mendapatkan akses hanya ke sandbox-nya sendiri dan ke koleksi file media tertentu melalui MediaStore.
Sebelum Scoped Storage, aplikasi apa pun dengan izin READ_EXTERNAL_STORAGE dapat membaca semua file di penyimpanan bersama — foto, dokumen, unduhan, data aplikasi lain. Ini menciptakan risiko privasi: aplikasi tidak jujur dapat mengumpulkan informasi tentang pengguna tanpa sepengetahuannya.
Menurut Google I/O 2019, lebih dari 65% pengguna Android menganggap privasi data sebagai faktor kritis saat memilih aplikasi. Scoped Storage adalah jawaban langsung atas permintaan ini: setiap aplikasi bekerja di lingkungan terisolasi dan mendapatkan akses ke data orang lain hanya dengan persetujuan eksplisit pengguna.
Penting: Scoped Storage di Android 10 bersifat opsional (aplikasi dapat menonaktifkannya melalui requestLegacyExternalStorage). Mulai Android 11 (API 30), Scoped Storage menjadi wajib untuk semua aplikasi, terlepas dari target SDK.
Google memperkenalkan Scoped Storage untuk menyelesaikan tiga masalah fundamental model akses file lama: privasi, manajemen izin, dan pembersihan file sisa.
Dalam model lama, aplikasi dengan READ_EXTERNAL_STORAGE dapat memindai seluruh penyimpanan bersama dan mengumpulkan metadata tentang file pengguna — koordinat geografis foto, nama dokumen, struktur direktori. Scoped Storage menghilangkan kemungkinan ini: bahkan dengan izin, aplikasi hanya melihat file yang dibuatnya sendiri dan file yang dipilih secara eksplisit oleh pengguna melalui SAF.
Sebelum Scoped Storage, aplikasi dapat meninggalkan file di penyimpanan bersama setelah dihapus. Seiring waktu, direktori sampah menumpuk. Scoped Storage mengatasi ini: semua file di sandbox aplikasi dihapus bersama aplikasi, dan file melalui MediaStore memiliki pemilik dan dapat dibersihkan oleh sistem.
| Aspek | Model lama (Legacy) | Scoped Storage |
|---|---|---|
| Akses ke penyimpanan bersama | Penuh (dengan izin) | Hanya sandbox sendiri |
| File media | Jalur file langsung | Melalui URI MediaStore |
| File orang lain | Selalu tersedia | Hanya melalui SAF |
| Pembersihan saat penghapusan | File tetap ada | Sandbox dihapus |
| Kontrol pengguna | Minimal | Persetujuan eksplisit per file |
Scoped Storage adalah bagian dari strategi umum Google untuk memperkuat privasi di Android, yang juga mencakup izin “hanya saat digunakan”, indikator akses kamera dan mikrofon, dan Privacy Dashboard.
Setiap aplikasi di Android mendapatkan direktori privat sendiri, dapat diakses melalui Context.getFilesDir() dan context.getCacheDir(). Untuk akses ke direktori privat eksternal (bersama) digunakan Context.getExternalFilesDir().
Di Scoped Storage, aplikasi memiliki akses penuh ke direktori privat eksternalnya tanpa izin apa pun. Ini adalah tempat utama untuk menyimpan file yang tidak boleh diakses oleh aplikasi lain atau yang dibuat aplikasi untuk penggunaannya sendiri.
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()
}
}
Jalur ke direktori privat eksternal: /storage/emulated/0/Android/data/{packageName}/files/. Mulai Android 11, jalur file langsung tidak tersedia — hanya melalui API. Ini adalah penguatan keamanan lain dari Scoped Storage.
MediaStore — adalah API utama untuk akses ke file media bersama (gambar, video, audio) di Scoped Storage. Aplikasi bekerja bukan dengan jalur file, tetapi dengan content URIs yang disediakan oleh penyedia MediaStore.
MediaStore dibagi menjadi tiga koleksi utama: Images, Video dan Audio. Setiap koleksi mendukung operasi CRUD melalui ContentResolver. Untuk menyisipkan file baru ke koleksi Images digunakan MediaStore.Images.Media, untuk menanyakan yang sudah ada — query dengan URI yang sesuai.
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
)
}
}
Penting: MediaStore tidak mendukung jalur file sembarang — hanya content URIs. Upaya mendapatkan jalur file dari URI melalui MediaStore.Files.getContentUri() tidak akan memberikan jalur langsung ke file di Android 11+. Sebagai gantinya, gunakan ContentResolver.openInputStream() dan openOutputStream() untuk bekerja dengan konten.
Storage Access Framework (SAF) — adalah API untuk akses ke file dan direktori sembarang di luar sandbox aplikasi. SAF menyediakan antarmuka pemilihan file atau direktori kepada pengguna, setelah itu aplikasi menerima content URI dengan akses sementara.
SAF digunakan untuk skenario yang tidak tercakup oleh MediaStore: bekerja dengan dokumen sembarang (PDF, ZIP, APK), akses ke direktori di kartu SD, impor dan ekspor file dari aplikasi lain. Pengguna secara eksplisit memilih file melalui pemilih file sistem — ini menjamin persetujuannya untuk akses.
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
// Dapatkan izin URI persisten
val flags = Intent.FLAG_GRANT_READ_URI_PERMISSION or
Intent.FLAG_GRANT_WRITE_URI_PERMISSION
contentResolver.takePersistableUriPermission(treeUri, flags)
}
}
SAF memberikan aplikasi akses ke direktori yang dipilih pengguna hingga perangkat di-restart (saat menggunakan takePersistableUriPermission). Ini adalah satu-satunya cara untuk mendapatkan akses ke file sembarang di penyimpanan bersama pada Android 11+.
Migrasi aplikasi Android yang ada ke Scoped Storage memerlukan perubahan di beberapa area kunci. Google merekomendasikan pendekatan bertahap dengan pengujian pada Android 11+.
Ganti semua operasi File langsung di penyimpanan bersama dengan ContentResolver.openInputStream / openOutputStream. Untuk file di sandbox aplikasi (getExternalFilesDir), jalur file tetap berfungsi.
Hapus READ_EXTERNAL_STORAGE dan WRITE_EXTERNAL_STORAGE jika tidak diperlukan untuk skenario spesifik (misalnya, manajemen file — untuk itu ada izin khusus MANAGE_EXTERNAL_STORAGE).
| Skenario | Pendekatan lama | Pendekatan Scoped Storage |
|---|---|---|
| Menyimpan foto | File(path).writeBytes() | MediaStore + ContentResolver |
| Membaca PDF | File(path).inputStream() | SAF ACTION_OPEN_DOCUMENT |
| File sendiri | Environment.getExternalStorageDirectory() | context.getExternalFilesDir() |
| Cache | File(cacheDir).writeBytes() | context.cacheDir (tidak berubah) |
Setelah migrasi, uji aplikasi pada Android 11+ (API 30) dengan menetapkan targetSdk = 30 atau lebih tinggi di build.gradle. Pastikan semua operasi file di penyimpanan bersama bekerja melalui MediaStore atau SAF, bukan melalui jalur file langsung.
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()
}
}
Menurut Google Play Console (2024), lebih dari 78% perangkat Android aktif berjalan pada Android 10+ dengan Scoped Storage. Migrasi adalah langkah wajib untuk mempublikasikan pembaruan di Google Play: aplikasi baru harus menargetkan API 31+, pembaruan — API 30+.
Pertanyaan yang Sering Diajukan
Tidak, mulai Android 11 (API 30) Scoped Storage wajib untuk semua aplikasi. Flag requestLegacyExternalStorage, yang tersedia di Android 10, tidak berfungsi pada API 30+. Satu-satunya cara untuk mendapatkan akses luas ke sistem file adalah izin MANAGE_EXTERNAL_STORAGE, tetapi ini hanya diperuntukkan bagi manajer file dan antivirus.
MANAGE_EXTERNAL_STORAGE — izin khusus untuk aplikasi yang memerlukan akses penuh ke sistem file (manajer file, alat backup). Permintaan izin membuka layar sistem dengan peringatan untuk pengguna. Saat mempublikasikan di Google Play, diperlukan pengisian formulir Declaration tentang perlunya izin ini.
Di Scoped Storage pada Android 11+, jalur file langsung dari content URI tidak dapat diperoleh. Sebagai gantinya, gunakan ContentResolver.openInputStream() untuk membaca dan openOutputStream() untuk menulis. Jika Anda memerlukan jalur file untuk kompatibilitas dengan pustaka pihak ketiga — buat salinan file di getCacheDir() dan bekerja dengan salinan.
FileProvider terus bekerja tanpa perubahan — digunakan untuk memberikan akses ke file dari sandbox aplikasi ke aplikasi lain melalui content URIs. Scoped Storage tidak mempengaruhi FileProvider, karena FileProvider bekerja pada tingkat content URIs, bukan jalur file langsung.
Periksa Build.VERSION.SDK_INT: jika >= Build.VERSION_CODES.Q (29), perangkat mendukung Scoped Storage. Namun, di Android 10, Scoped Storage dapat dinonaktifkan melalui requestLegacyExternalStorage. Di Android 11+, periksa apakah flag requestLegacyExternalStorage diatur di manifes — jika tidak, Scoped Storage aktif.
Ringkasan
Kami akan mengembangkan aplikasi seluler turnkey
IT Sectr membuat aplikasi iOS dan Android untuk startup dan bisnis sejak 2017. Kami akan memberi saran dan mengusulkan solusi terbaik.
Baca juga