Scoped Storage — Android 10-da (API 29) təqdim edilmiş, proqramların cihazın ümumi yaddaşına ixtiyari girişini məhdudlaşdıran fayl sisteminə giriş modelidir. Google Android Developer Documentation (2024) məlumatına görə, Scoped Storage READ_EXTERNAL_STORAGE və WRITE_EXTERNAL_STORAGE icazələrinin köhnə modelini əvəz edərək, proqramlara yalnız izolyasiya edilmiş qum qutusundakı öz fayllarına və MediaStore vasitəsilə ümumi kolleksiyalara giriş imkanı verir. Bu model istifadəçi məlumatlarının məxfiliyini artırır və proqramlar tərəfindən icazəsiz məlumat toplanmasının qarşısını alır.
Əsas məqamlar
Scoped Storage — Android 10-da (API 29) təqdim edilmiş, proqramların cihazın fayl sisteminə giriş üsulunu əsaslı şəkildə dəyişən memarlıq dəyişikliyidir. Ümumi yaddaşa tam giriş əvəzinə, proqram yalnız öz qum qutusuna və MediaStore vasitəsilə müəyyən media fayl kolleksiyalarına giriş əldə edir.
Scoped Storage-dən əvvəl READ_EXTERNAL_STORAGE icazəsi olan istənilən proqram ümumi yaddaşdakı bütün faylları — şəkilləri, sənədləri, yükləmələri, digər proqramların məlumatlarını oxuya bilirdi. Bu, məxfilik riskləri yaradırdı: vicdansız proqramlar istifadəçinin xəbəri olmadan onun haqqında məlumat toplaya bilərdi.
Google I/O 2019 məlumatına görə, Android istifadəçilərinin 65%-dən çoxu proqram seçərkən məlumat məxfiliyini kritik amil hesab edir. Scoped Storage bu tələbə birbaşa cavabdır: hər bir proqram izolyasiya edilmiş mühitdə işləyir və digər proqramların məlumatlarına yalnız istifadəçinin açıq razılığı ilə giriş əldə edir.
Vacib: Android 10-da Scoped Storage isteğe bağlı idi (proqram onu requestLegacyExternalStorage vasitəsilə söndürə bilərdi). Android 11-dən (API 30) başlayaraq, Scoped Storage target SDK-dan asılı olmayaraq bütün proqramlar üçün məcburi oldu.
Google Scoped Storage-i köhnə fayl giriş modelinin üç fundamental problemini həll etmək üçün təqdim etdi: məxfilik, icazələrin idarə edilməsi və qalıq faylların təmizlənməsi.
Köhnə modeldə READ_EXTERNAL_STORAGE icazəsi olan proqram bütün ümumi yaddaşı skan edə və istifadəçinin faylları haqqında metadata toplaya bilərdi — fotoşəkillərin coğrafi koordinatları, sənəd adları, qovluq strukturu. Scoped Storage bu imkanı aradan qaldırır: icazəyə malik olsa belə, proqram yalnız özünün yaratdığı faylları və istifadəçinin SAF vasitəsilə açıq seçdiyi faylları görür.
Scoped Storage-dən əvvəl proqram silindikdən sonra ümumi yaddaşda fayllar qoya bilərdi. Zaman keçdikcə zibil qovluqlar yığılırdı. Scoped Storage bunu həll edir: proqramın qum qutusundakı bütün fayllar proqramla birlikdə silinir, MediaStore vasitəsilə fayllar isə sahibinə malikdir və sistem tərəfindən təmizlənə bilər.
| Aspekt | Köhnə model (Legacy) | Scoped Storage |
|---|---|---|
| Ümumi yaddaşa giriş | Tam (icazə ilə) | Yalnız öz qum qutusu |
| Media faylları | Birbaşa fayl yolu | MediaStore URI vasitəsilə |
| Başqalarının faylları | Həmişə əlçatan | Yalnız SAF vasitəsilə |
| Silinmədə təmizləmə | Fayllar qalır | Qum qutusu silinir |
| İstifadəçi nəzarəti | Minimal | Hər fayl üçün açıq razılıq |
Scoped Storage Google-nin Android-də məxfiliyi gücləndirmək üzrə ümumi strategiyasının bir hissəsidir ki, bura həmçinin „yalnız istifadə zamanı” icazələri, kamera və mikrofona giriş göstəriciləri və Privacy Dashboard daxildir.
Hər Android proqramı Context.getFilesDir() və context.getCacheDir() vasitəsilə əldə edilə bilən öz şəxsi qovluğunu alır. Xarici (paylaşılan) şəxsi qovluğa giriş üçün Context.getExternalFilesDir() istifadə olunur.
Scoped Storage-də proqram heç bir icazə olmadan öz xarici şəxsi qovluğuna tam girişə malikdir. Bu, digər proqramlar üçün əlçatan olmamalı və ya proqramın öz istifadəsi üçün yaratdığı faylları saxlamaq üçün əsas yerdir.
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()
}
}
Xarici şəxsi qovluğa yol: /storage/emulated/0/Android/data/{packageName}/files/. Android 11-dən başlayaraq birbaşa fayl yolu mövcud deyil — yalnız API vasitəsilə. Bu, Scoped Storage-in təhlükəsizliyinin daha bir gücləndirilməsidir.
MediaStore — Scoped Storage-də ümumi media fayllarına (şəkillər, video, audio) giriş üçün əsas API-dir. Proqram fayl yolları ilə deyil, MediaStore provider tərəfindən təmin edilən content URIs ilə işləyir.
MediaStore üç əsas kolleksiyaya bölünür: Images, Video və Audio. Hər bir kolleksiya ContentResolver vasitəsilə CRUD əməliyyatlarını dəstəkləyir. Images kolleksiyasına yeni fayl daxil etmək üçün MediaStore.Images.Media, mövcud olanları sorğulamaq üçün müvafiq URI ilə query istifadə olunur.
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
)
}
}
Vacib: MediaStore ixtiyari fayl yollarını dəstəkləmir — yalnız content URIs. MediaStore.Files.getContentUri() vasitəsilə URI-dən fayl yolu almaq cəhdi Android 11+-da fayla birbaşa yol verməyəcək. Bunun əvəzinə məzmunla işləmək üçün ContentResolver.openInputStream() və openOutputStream() istifadə edin.
Storage Access Framework (SAF) — proqramın qum qutusundan kənarda ixtiyari fayl və qovluqlara giriş üçün API-dir. SAF istifadəçiyə fayl və ya qovluq seçmək interfeysi təqdim edir, bundan sonra proqram müvəqqəti girişi olan content URI əldə edir.
SAF MediaStore tərəfindən əhatə olunmayan ssenarilər üçün istifadə olunur: ixtiyari sənədlərlə (PDF, ZIP, APK) işləmək, SD kartdakı qovluqlara giriş, digər proqramlardan faylların idxalı və ixracı. İstifadəçi sistematik fayl seçici vasitəsilə faylı açıq şəkildə seçir — bu, onun girişə razılığını təmin edir.
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
// Daimi URI icazəsini əldə edin
val flags = Intent.FLAG_GRANT_READ_URI_PERMISSION or
Intent.FLAG_GRANT_WRITE_URI_PERMISSION
contentResolver.takePersistableUriPermission(treeUri, flags)
}
}
SAF proqrama istifadəçi tərəfindən seçilmiş qovluğa cihaz yenidən başladılana qədər giriş imkanı verir (takePersistableUriPermission istifadə etdikdə). Bu, Android 11+-da ümumi yaddaşda ixtiyari fayllara giriş əldə etməyin yeganə yoludur.
Mövcud Android proqramını Scoped Storage-ə köçürmək bir neçə əsas sahədə dəyişiklik tələb edir. Google Android 11+-da sınaqdan keçirməklə mərhələli yanaşma tövsiyə edir.
Ümumi yaddaşdakı bütün birbaşa File əməliyyatlarını ContentResolver.openInputStream / openOutputStream ilə əvəz edin. Proqramın qum qutusundakı fayllar (getExternalFilesDir) üçün fayl yolları işləməyə davam edir.
Xüsusi ssenarilər üçün lazım deyilsə (məsələn, fayl idarəetməsi — bunun üçün xüsusi MANAGE_EXTERNAL_STORAGE icazəsi var), READ_EXTERNAL_STORAGE və WRITE_EXTERNAL_STORAGE-i silin.
| Ssenari | Köhnə yanaşma | Scoped Storage yanaşması |
|---|---|---|
| Şəklin saxlanması | File(path).writeBytes() | MediaStore + ContentResolver |
| PDF-in oxunması | File(path).inputStream() | SAF ACTION_OPEN_DOCUMENT |
| Öz faylları | Environment.getExternalStorageDirectory() | context.getExternalFilesDir() |
| Keş | File(cacheDir).writeBytes() | context.cacheDir (dəyişiklik yoxdur) |
Köçürmədən sonra proqramı build.gradle-də targetSdk = 30 və ya daha yüksək təyin edərək Android 11+ (API 30)-da sınaqdan keçirin. Ümumi yaddaşdakı bütün fayl əməliyyatlarının birbaşa fayl yolları ilə deyil, MediaStore və ya SAF vasitəsilə işlədiyinə əmin olun.
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) məlumatına görə, aktiv Android cihazlarının 78%-dən çoxu Scoped Storage ilə Android 10+-da işləyir. Köçürmə Google Play-də yeniləmələrin dərc edilməsi üçün məcburi addımdır: yeni proqramlar API 31+ hədəfləməlidir, yeniləmələr — API 30+.
Tez-tez verilən suallar
Xeyr, Android 11-dən (API 30) başlayaraq Scoped Storage bütün proqramlar üçün məcburidir. Android 10-da mövcud olan requestLegacyExternalStorage bayrağı API 30+-da işləmir. Fayl sisteminə geniş giriş əldə etməyin yeganə yolu MANAGE_EXTERNAL_STORAGE icazəsidir, lakin o, yalnız fayl menecerləri və antiviruslar üçün nəzərdə tutulub.
MANAGE_EXTERNAL_STORAGE — fayl sisteminə tam girişə ehtiyacı olan proqramlar (fayl menecerləri, backup alətləri) üçün xüsusi icazədir. İcazə sorğusu istifadəçi üçün xəbərdarlıqla sistem ekranını açır. Google Play-də dərc edərkən bu icazənin zəruriliyi barədə Declaration formasını doldurmaq tələb olunur.
Scoped Storage-də Android 11+-da content URI-dən birbaşa fayl yolu əldə etmək mümkün deyil. Bunun əvəzinə oxumaq üçün ContentResolver.openInputStream(), yazmaq üçün openOutputStream() istifadə edin. Üçüncü tərəf kitabxanası ilə uyğunluq üçün fayl yoluna ehtiyacınız varsa — getCacheDir()-də faylın surətini yaradın və surət ilə işləyin.
FileProvider dəyişiklik olmadan işləməyə davam edir — o, proqramın qum qutusundan faylları content URIs vasitəsilə digər proqramlara təqdim etmək üçün istifadə olunur. Scoped Storage FileProvider-a təsir etmir, çünki FileProvider birbaşa fayl yolları deyil, content URIs səviyyəsində işləyir.
Build.VERSION.SDK_INT-i yoxlayın: əgər >= Build.VERSION_CODES.Q (29) olarsa, cihaz Scoped Storage-i dəstəkləyir. Lakin Android 10-da Scoped Storage requestLegacyExternalStorage vasitəsilə söndürülə bilər. Android 11+-da manifestdə requestLegacyExternalStorage bayrağının qoyulub-qoyulmadığını yoxlayın — əgər yoxdursa, Scoped Storage aktivdir.
Nəticə
Açar təslim mobil tətbiq hazırlayacağıq
IT Sectr 2017-ci ildən startaplar və bizneslər üçün iOS və Android tətbiqləri yaradır. Sizə məsləhət verəcəyik və ən yaxşı həlli təklif edəcəyik.
Həm də oxuyun