Scoped Storage — چیست، مدیریت فایل‌ها و دسترسی به حافظه

نویسنده: IT Sectr منتشر شده: 2026-07-10 زمان مطالعه: 10 دقیقه

Scoped Storage — مدل دسترسی به سیستم فایل است که در اندروید ۱۰ (API 29) معرفی شد و دسترسی دلخواه برنامه‌ها به حافظه مشترک دستگاه را محدود می‌کند. طبق Google Android Developer Documentation (2024)، Scoped Storage مدل قدیمی مجوزهای READ_EXTERNAL_STORAGE و WRITE_EXTERNAL_STORAGE را جایگزین می‌کند و به برنامه‌ها فقط به فایل‌های خودشان در محیط ایزوله و به مجموعه‌های مشترک از طریق MediaStore دسترسی می‌دهد. این مدل حریم خصوصی داده‌های کاربر را افزایش می‌دهد و از جمع‌آوری غیرمجاز اطلاعات توسط برنامه‌ها جلوگیری می‌کند.

نکات اصلی

  • Scoped Storage — مدل دسترسی به فایل‌ها در اندروید ۱۰+ که خواندن دلخواه حافظه مشترک را محدود می‌کند.
  • برنامه دارای محفظه اختصاصی خود با دسترسی کامل بدون مجوزهای اضافی است.
  • برای دسترسی به فایل‌های مشترک (عکس، ویدئو، صدا) از MediaStore API استفاده می‌شود.
  • برای دسترسی به دایرکتوری‌ها و فایل‌های دلخواه از Storage Access Framework (SAF) استفاده می‌شود.
  • مجوزهای READ_EXTERNAL_STORAGE و WRITE_EXTERNAL_STORAGE برای بیشتر سناریوها اعتبار خود را از دست داده‌اند.

Scoped Storage در اندروید چیست؟

Scoped Storage — یک تغییر معماری در اندروید ۱۰ (API 29) است که نحوه دسترسی برنامه‌ها به سیستم فایل دستگاه را به صورت اساسی تغییر می‌دهد. به جای دسترسی کامل به حافظه مشترک، برنامه فقط به محفظه خود و مجموعه‌های مشخصی از فایل‌های رسانه‌ای از طریق MediaStore دسترسی پیدا می‌کند.

پیش از Scoped Storage، هر برنامه‌ای با مجوز READ_EXTERNAL_STORAGE می‌توانست همه فایل‌های حافظه مشترک — عکس‌ها، اسناد، دانلودها، داده‌های سایر برنامه‌ها — را بخواند. این امر خطرات حریم خصوصی ایجاد می‌کرد: برنامه‌های غیرصادق می‌توانستند بدون اطلاع کاربر اطلاعاتی درباره او جمع‌آوری کنند.

طبق Google I/O 2019، بیش از ۶۵٪ کاربران اندروید حریم خصوصی داده‌ها را عامل حیاتی در انتخاب برنامه می‌دانند. Scoped Storage پاسخ مستقیم به این نیاز است: هر برنامه در محیطی ایزوله کار می‌کند و فقط با رضایت صریح کاربر به داده‌های دیگران دسترسی پیدا می‌کند.

مهم: Scoped Storage در اندروید ۱۰ اختیاری بود (برنامه می‌توانست آن را از طریق requestLegacyExternalStorage غیرفعال کند). از اندروید ۱۱ (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
دسترسی به حافظه مشترککامل (با مجوز)فقط محفظه خود
فایل‌های رسانه‌ایمسیر فایل مستقیماز طریق URI MediaStore
فایل‌های دیگرانهمیشه در دسترسفقط از طریق SAF
پاکسازی هنگام حذففایل‌ها باقی می‌مانندمحفظه حذف می‌شود
کنترل کاربرحداقلیرضایت صریح برای هر فایل

Scoped Storage بخشی از استراتژی کلی Google برای تقویت حریم خصوصی در اندروید است که شامل مجوزهای «فقط هنگام استفاده»، نشانگرهای دسترسی به دوربین و میکروفون و Privacy Dashboard نیز می‌شود.

دایرکتوری خصوصی برنامه

هر برنامه در اندروید یک دایرکتوری خصوصی خود را از طریق 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/. از اندروید ۱۱ به بعد مسیر مستقیم فایل در دسترس نیست — فقط از طریق 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() در اندروید ۱۱+ مسیر مستقیمی به فایل نمی‌دهد. به جای آن از 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). این تنها راه برای دریافت دسترسی به فایل‌های دلخواه در حافظه مشترک در اندروید ۱۱+ است.

مهاجرت برنامه به Scoped Storage

مهاجرت برنامه اندرویدی موجود به Scoped Storage نیازمند تغییرات در چندین حوزه کلیدی است. Google رویکرد مرحله‌ای با آزمایش در اندروید ۱۱+ را توصیه می‌کند.

جایگزینی مسیرهای فایل با content URIs

همه عملیات مستقیم File در حافظه مشترک را با ContentResolver.openInputStream / openOutputStream جایگزین کنید. برای فایل‌های موجود در محفظه برنامه (getExternalFilesDir) مسیرهای فایل همچنان کار می‌کنند.

به‌روزرسانی AndroidManifest

READ_EXTERNAL_STORAGE و WRITE_EXTERNAL_STORAGE را حذف کنید، مگر اینکه برای سناریوهای خاصی مورد نیاز باشند (مثلاً مدیریت فایل — برای این کار مجوز ویژه MANAGE_EXTERNAL_STORAGE وجود دارد).

سناریورویکرد قدیمیرویکرد Scoped Storage
ذخیره عکسFile(path).writeBytes()MediaStore + ContentResolver
خواندن PDFFile(path).inputStream()SAF ACTION_OPEN_DOCUMENT
فایل‌های خودEnvironment.getExternalStorageDirectory()context.getExternalFilesDir()
حافظه نهانFile(cacheDir).writeBytes()context.cacheDir (بدون تغییر)

پس از مهاجرت، برنامه را در اندروید ۱۱+ (API 30) با تنظیم targetSdk = 30 یا بالاتر در build.gradle آزمایش کنید. اطمینان حاصل کنید که همه عملیات فایل در حافظه مشترک از طریق 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)، بیش از ۷۸٪ دستگاه‌های فعال اندرویدی با Scoped Storage روی اندروید ۱۰+ کار می‌کنند. مهاجرت یک مرحله اجباری برای انتشار به‌روزرسانی‌ها در Google Play است: برنامه‌های جدید باید API 31+ را هدف قرار دهند، به‌روزرسانی‌ها — API 30+.

سوالات متداول

آیا می‌توان Scoped Storage را در اندروید ۱۱ غیرفعال کرد؟

خیر، از اندروید ۱۱ (API 30) به بعد Scoped Storage برای همه برنامه‌ها اجباری است. پرچم requestLegacyExternalStorage که در اندروید ۱۰ در دسترس بود، در API 30+ کار نمی‌کند. تنها راه برای دریافت دسترسی گسترده به سیستم فایل، مجوز MANAGE_EXTERNAL_STORAGE است، اما فقط برای مدیران فایل و آنتی‌ویروس‌ها در نظر گرفته شده است.

MANAGE_EXTERNAL_STORAGE چیست و چگونه آن را دریافت کنیم؟

MANAGE_EXTERNAL_STORAGE — مجوز ویژه برای برنامه‌هایی که نیاز به دسترسی کامل به سیستم فایل دارند (مدیران فایل، ابزارهای پشتیبان‌گیری). درخواست مجوز یک صفحه سیستم با هشدار برای کاربر باز می‌کند. هنگام انتشار در Google Play لازم است فرم Declaration در مورد نیاز به این مجوز پر شود.

چگونه مسیر فایل را از content URI در Scoped Storage دریافت کنیم؟

در Scoped Storage در اندروید ۱۱+ نمی‌توان مسیر مستقیم فایل از 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 پشتیبانی می‌کند. اما در اندروید ۱۰، Scoped Storage می‌تواند از طریق requestLegacyExternalStorage غیرفعال شود. در اندروید ۱۱+ بررسی کنید که پرچم requestLegacyExternalStorage در مانیفست تنظیم شده است یا خیر — اگر نه، Scoped Storage فعال است.

خلاصه

  • Scoped Storage — مدل دسترسی به فایل‌ها در اندروید ۱۰+ که خواندن دلخواه حافظه مشترک را محدود می‌کند.
  • برنامه بدون مجوزهای اضافی به محفظه خصوصی خود دسترسی کامل دارد.
  • برای دسترسی به فایل‌های رسانه‌ای مشترک از MediaStore API با content URIs استفاده می‌شود.
  • برای دسترسی به فایل‌ها و دایرکتوری‌های دلخواه از Storage Access Framework (SAF) استفاده می‌شود.
  • از اندروید ۱۱ به بعد Scoped Storage برای همه برنامه‌ها اجباری است.
  • مجوزهای READ_EXTERNAL_STORAGE و WRITE_EXTERNAL_STORAGE در بیشتر سناریوها در اندروید ۱۱+ کار نمی‌کنند.
  • در مهاجرت، مسیرهای فایل مستقیم را با ContentResolver.openInputStream / openOutputStream جایگزین کنید.

ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد

IT Sectr از سال 2017 برنامه‌های iOS و Android را برای استارتاپ‌ها و کسب‌وکارها ایجاد می‌کند. ما به شما مشاوره می‌دهیم و بهترین راه‌حل را پیشنهاد خواهیم کرد.

بحث درباره پروژه

همچنین بخوانید