Scoped Storage — Android 10 (API 29) da joriy qilingan, ilovalarning qurilma umumiy xotirasiga o‘zboshimchalik bilan kirishini cheklovchi fayl tizimiga kirish modelidir. Google Android Developer Documentation (2024) ma’lumotlariga ko‘ra, Scoped Storage READ_EXTERNAL_STORAGE va WRITE_EXTERNAL_STORAGE ruxsatlarining eski modelini almashtiradi va ilovalarga faqat izolyatsiya qilingan qum qutisidagi o‘z fayllariga va MediaStore orqali umumiy to‘plamlarga kirish imkonini beradi. Ushbu model foydalanuvchi ma’lumotlarining maxfiyligini oshiradi va ilovalar tomonidan ruxsatsiz ma’lumot to‘planishining oldini oladi.
Asosiy ma’lumotlar
Scoped Storage — Android 10 (API 29) da ilovalarning qurilma fayl tizimiga kirish usulini tubdan o‘zgartiruvchi arxitekturaviy o‘zgarishdir. Umumiy xotiraga to‘liq kirish o‘rniga, ilova faqat o‘z qum qutisiga va MediaStore orqali ma’lum media fayl to‘plamlariga kirish imkoniyatiga ega bo‘ladi.
Scoped Storage dan oldin READ_EXTERNAL_STORAGE ruxsatiga ega har qanday ilova umumiy xotiradagi barcha fayllarni — fotosuratlar, hujjatlar, yuklab olinganlar, boshqa ilovalarning ma’lumotlarini o‘qiy olardi. Bu maxfiylik xavfini tug‘dirardi: insofsiz ilovalar foydalanuvchining xabarisiz u haqida ma’lumot to‘plashi mumkin edi.
Google I/O 2019 ma’lumotlariga ko‘ra, Android foydalanuvchilarining 65% dan ortig‘i ilova tanlashda ma’lumotlar maxfiyligini muhim omil deb biladi. Scoped Storage bu talabga to‘g‘ridan-to‘g‘ri javobdir: har bir ilova izolyatsiya qilingan muhitda ishlaydi va boshqa ilovalarning ma’lumotlariga faqat foydalanuvchining aniq roziligi bilan kirishadi.
Muhim: Android 10 da Scoped Storage ixtiyoriy edi (ilova uni requestLegacyExternalStorage orqali o‘chirib qo‘yishi mumkin edi). Android 11 (API 30) dan boshlab, Scoped Storage target SDK dan qat’iy nazar barcha ilovalar uchun majburiy bo‘ldi.
Google Scoped Storage ni eski faylga kirish modelining uchta asosiy muammosini hal qilish uchun joriy qildi: maxfiylik, ruxsatlarni boshqarish va qoldiq fayllarni tozalash.
Eski modelda READ_EXTERNAL_STORAGE ruxsatiga ega ilova butun umumiy xotirani skanerlashi va foydalanuvchining fayllari haqida metama’lumot to‘plashi mumkin edi — fotosuratlarning geografik koordinatalari, hujjat nomlari, katalog tuzilmasi. Scoped Storage bu imkoniyatni bartaraf etadi: ruxsatga ega bo‘lsa ham, ilova faqat o‘zi yaratgan fayllarni va foydalanuvchi SAF orqali aniq tanlagan fayllarni ko‘radi.
Scoped Storage dan oldin ilova o‘chirilgandan so‘ng umumiy xotiraga fayllarni qoldirishi mumkin edi. Vaqt o‘tishi bilan axlat kataloglari to‘planardi. Scoped Storage buni hal qiladi: ilovaning qum qutisidagi barcha fayllar ilova bilan birga o‘chiriladi, MediaStore orqali fayllar esa egasiga ega va tizim tomonidan tozalanishi mumkin.
| Aspekt | Eski model (Legacy) | Scoped Storage |
|---|---|---|
| Umumiy xotiraga kirish | To‘liq (ruxsat bilan) | Faqat o‘z qum qutisi |
| Media fayllar | To‘g‘ridan-to‘g‘ri fayl yo‘li | MediaStore URI orqali |
| Boshqalarning fayllari | Har doim mavjud | Faqat SAF orqali |
| O‘chirishda tozalash | Fayllar qoladi | Qum qutisi o‘chiriladi |
| Foydalanuvchi nazorati | Minimal | Har bir fayl uchun aniq rozilik |
Scoped Storage Google ning Android da maxfiylikni kuchaytirish bo‘yicha umumiy strategiyasining bir qismidir, bunga shuningdek “faqat foydalanish vaqtida” ruxsatlari, kamera va mikrofonga kirish ko‘rsatkichlari va Privacy Dashboard kiradi.
Har bir Android ilovasi Context.getFilesDir() va context.getCacheDir() orqali mavjud bo‘lgan o‘z shaxsiy katalogini oladi. Tashqi (umumiy) shaxsiy katalogga kirish uchun Context.getExternalFilesDir() ishlatiladi.
Scoped Storage da ilova hech qanday ruxsatsiz o‘zining tashqi shaxsiy katalogiga to‘liq kirish huquqiga ega. Bu boshqa ilovalar uchun mavjud bo‘lmasligi kerak bo‘lgan yoki ilova o‘z foydalanishi uchun yaratadigan fayllarni saqlash uchun asosiy joydir.
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()
}
}
Tashqi shaxsiy katalog yo‘li: /storage/emulated/0/Android/data/{packageName}/files/. Android 11 dan boshlab to‘g‘ridan-to‘g‘ri fayl yo‘li mavjud emas — faqat API orqali. Bu Scoped Storage xavfsizligining yana bir kuchaytirilishidir.
MediaStore — Scoped Storage da umumiy media fayllarga (tasvirlar, video, audio) kirish uchun asosiy API. Ilova fayl yo‘llari bilan emas, balki MediaStore provider tomonidan taqdim etilgan content URIs bilan ishlaydi.
MediaStore uchta asosiy to‘plamga bo‘linadi: Images, Video va Audio. Har bir to‘plam ContentResolver orqali CRUD operatsiyalarini qo‘llab-quvvatlaydi. Images to‘plamiga yangi fayl kiritish uchun MediaStore.Images.Media, mavjudlarini so‘rash uchun tegishli URI bilan query ishlatiladi.
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
)
}
}
Muhim: MediaStore o‘zboshimchalik bilan fayl yo‘llarini qo‘llab-quvvatlamaydi — faqat content URIs. MediaStore.Files.getContentUri() orqali URI dan fayl yo‘lini olish urinishi Android 11+ da faylga to‘g‘ridan-to‘g‘ri yo‘l bermaydi. Buning o‘rniga tarkib bilan ishlash uchun ContentResolver.openInputStream() va openOutputStream() dan foydalaning.
Storage Access Framework (SAF) — ilovaning qum qutisidan tashqaridagi o‘zboshimchalik bilan fayl va kataloglarga kirish uchun API. SAF foydalanuvchiga fayl yoki katalogni tanlash interfeysini taqdim etadi, shundan so‘ng ilova vaqtinchalik kirish huquqiga ega content URI oladi.
SAF MediaStore qamrab olmagan stsenariylar uchun ishlatiladi: o‘zboshimchalik hujjatlar (PDF, ZIP, APK) bilan ishlash, SD karta kataloglariga kirish, boshqa ilovalardan fayllarni import va eksport qilish. Foydalanuvchi tizim fayl tanlovchi orqali faylni aniq tanlaydi — bu uning kirishga roziligini kafolatlaydi.
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
// Doimiy URI ruxsatini oling
val flags = Intent.FLAG_GRANT_READ_URI_PERMISSION or
Intent.FLAG_GRANT_WRITE_URI_PERMISSION
contentResolver.takePersistableUriPermission(treeUri, flags)
}
}
SAF ilovaga foydalanuvchi tomonidan tanlangan katalogga qurilma qayta ishga tushirilgunga qadar kirish imkonini beradi (takePersistableUriPermission dan foydalanganda). Bu Android 11+ da umumiy xotirada o‘zboshimchalik bilan fayllarga kirishning yagona yo‘lidir.
Mavjud Android ilovasini Scoped Storage ga ko‘chirish bir necha asosiy sohalarda o‘zgartirishlarni talab qiladi. Google Android 11+ da sinovdan o‘tkazish bilan bosqichma-bosqich yondashuvni tavsiya qiladi.
Umumiy xotiradagi barcha to‘g‘ridan-to‘g‘ri File operatsiyalarini ContentResolver.openInputStream / openOutputStream bilan almashtiring. Ilovaning qum qutisidagi fayllar (getExternalFilesDir) uchun fayl yo‘llari ishlashda davom etadi.
Agar maxsus stsenariylar uchun kerak bo‘lmasa (masalan, fayl boshqaruvi — buning uchun maxsus MANAGE_EXTERNAL_STORAGE ruxsati mavjud), READ_EXTERNAL_STORAGE va WRITE_EXTERNAL_STORAGE ni olib tashlang.
| Stsenariy | Eski yondashuv | Scoped Storage yondashuvi |
|---|---|---|
| Fotosuratni saqlash | File(path).writeBytes() | MediaStore + ContentResolver |
| PDF ni o‘qish | File(path).inputStream() | SAF ACTION_OPEN_DOCUMENT |
| O‘z fayllari | Environment.getExternalStorageDirectory() | context.getExternalFilesDir() |
| Kesh | File(cacheDir).writeBytes() | context.cacheDir (o‘zgarishsiz) |
Ko‘chirishdan so‘ng ilovani build.gradle da targetSdk = 30 yoki undan yuqori o‘rnatib, Android 11+ (API 30) da sinab ko‘ring. Umumiy xotiradagi barcha fayl operatsiyalari to‘g‘ridan-to‘g‘ri fayl yo‘llari orqali emas, balki MediaStore yoki SAF orqali ishlashiga ishonch hosil qiling.
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) ma’lumotlariga ko‘ra, faol Android qurilmalarining 78% dan ortig‘i Scoped Storage bilan Android 10+ da ishlaydi. Ko‘chirish Google Play da yangilanishlarni nashr qilish uchun majburiy qadamdir: yangi ilovalar API 31+, yangilanishlar API 30+ ni nishonga olishi kerak.
Tez-tez beriladigan savollar
Yo‘q, Android 11 (API 30) dan boshlab Scoped Storage barcha ilovalar uchun majburiydir. Android 10 da mavjud bo‘lgan requestLegacyExternalStorage flag‘i API 30+ da ishlamaydi. Fayl tizimiga keng kirishning yagona yo‘li MANAGE_EXTERNAL_STORAGE ruxsatidir, lekin u faqat fayl menejerlari va antiviruslar uchun mo‘ljallangan.
MANAGE_EXTERNAL_STORAGE — fayl tizimiga to‘liq kirishga muhtoj ilovalar (fayl menejerlari, backup vositalari) uchun maxsus ruxsatdir. Ruxsat so‘rovi foydalanuvchi uchun ogohlantirish bilan tizim ekranini ochadi. Google Play da nashr qilishda ushbu ruxsat zarurligi haqida Declaration formasini to‘ldirish talab qilinadi.
Scoped Storage da Android 11+ da content URI dan to‘g‘ridan-to‘g‘ri fayl yo‘lini olish mumkin emas. Buning o‘rniga o‘qish uchun ContentResolver.openInputStream(), yozish uchun openOutputStream() dan foydalaning. Agar uchinchi tomon kutubxonasi bilan moslik uchun fayl yo‘li kerak bo‘lsa — getCacheDir() da fayl nusxasini yarating va nusxa bilan ishlang.
FileProvider o‘zgarishsiz ishlashda davom etadi — u ilovaning qum qutisidagi fayllarga content URIs orqali boshqa ilovalarga kirishni ta’minlash uchun ishlatiladi. Scoped Storage FileProvider ga ta’sir qilmaydi, chunki FileProvider to‘g‘ridan-to‘g‘ri fayl yo‘llari emas, balki content URIs darajasida ishlaydi.
Build.VERSION.SDK_INT ni tekshiring: agar >= Build.VERSION_CODES.Q (29) bo‘lsa, qurilma Scoped Storage ni qo‘llab-quvvatlaydi. Biroq, Android 10 da Scoped Storage requestLegacyExternalStorage orqali o‘chirilgan bo‘lishi mumkin. Android 11+ da manifestda requestLegacyExternalStorage flag‘i o‘rnatilganligini tekshiring — agar yo‘q bo‘lsa, Scoped Storage faoldir.
Xulosa
Biz kalit topshirig'i bilan mobil ilovani ishlab chiqamiz
IT Sectr 2017-yildan beri startaplar va korxonalar uchun iOS va Android ilovalarini yaratadi. Biz sizga maslahat beramiz va eng yaxshi yechimni taklif qilamiz.