Scoped Storage — ما هو، إدارة الملفات والوصول إلى التخزين

المؤلف: IT Sectr نُشر: 2026-07-10 وقت القراءة: 10 دق

Scoped Storage هو نموذج للوصول إلى نظام الملفات تم تقديمه في Android 10 (API 29) ويقيد الوصول العشوائي للتطبيقات إلى التخزين المشترك للجهاز. وفقًا لوثائق مطوري Android من Google (2024)، يحل Scoped Storage محل نموذج الأذونات القديم READ_EXTERNAL_STORAGE و WRITE_EXTERNAL_STORAGE، مما يمنح التطبيقات حق الوصول فقط إلى ملفاتها الخاصة في بيئة معزولة وإلى المجموعات المشتركة عبر MediaStore. يعزز هذا النموذج خصوصية بيانات المستخدم ويمنع جمع المعلومات غير المصرح به من قبل التطبيقات.

الملامح الرئيسية

  • Scoped Storage هو نموذج للوصول إلى الملفات في Android 10+ يقيد القراءة العشوائية للتخزين المشترك.
  • التطبيق لديه بيئة معزولة خاصة مع وصول كامل دون أذونات إضافية.
  • للوصول إلى الملفات المشتركة (الصور، الفيديو، الصوت) يتم استخدام MediaStore API.
  • للوصول إلى الدلائل والملفات العشوائية يتم استخدام Storage Access Framework (SAF).
  • أذونات READ_EXTERNAL_STORAGE و WRITE_EXTERNAL_STORAGE فقدت فعاليتها لمعظم السيناريوهات.

ما هو Scoped Storage في Android؟

Scoped Storage هو تغيير معماري في Android 10 (API 29) يغير بشكل جذري طريقة وصول التطبيقات إلى نظام الملفات في الجهاز. بدلاً من الوصول الكامل إلى التخزين المشترك، يحصل التطبيق على حق الوصول فقط إلى بيئته المعزولة وإلى مجموعات وسائط محددة عبر MediaStore.

قبل Scoped Storage، كان بإمكان أي تطبيق لديه إذن READ_EXTERNAL_STORAGE قراءة جميع الملفات في التخزين المشترك — الصور والمستندات والتنزيلات وبيانات التطبيقات الأخرى. هذا خلق مخاطر على الخصوصية: يمكن للتطبيقات غير الجديرة بالثقة جمع معلومات عن المستخدم دون علمه.

وفقًا لـ Google I/O 2019، يعتبر أكثر من 65% من مستخدمي Android خصوصية البيانات عاملاً حاسمًا عند اختيار التطبيق. Scoped Storage هو استجابة مباشرة لهذا الطلب: يعمل كل تطبيق في بيئة معزولة ويحصل على حق الوصول إلى بيانات التطبيقات الأخرى فقط بموافقة صريحة من المستخدم.

هام: كان Scoped Storage في Android 10 اختياريًا (يمكن للتطبيق تعطيله عبر requestLegacyExternalStorage). بدءًا من Android 11 (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 في Android، والتي تشمل أيضًا أذونات "أثناء الاستخدام فقط" ومؤشرات الوصول إلى الكاميرا والميكروفون ولوحة تحكم الخصوصية.

الدليل الخاص بالتطبيق

كل تطبيق في Android يحصل على دليله الخاص الذي يمكن الوصول إليه عبر 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/. بدءًا من Android 11، مسار الملف المباشر غير متاح — فقط من خلال API. هذا تحسين أمني آخر لـ Scoped Storage.

MediaStore — الوصول إلى ملفات الوسائط

MediaStore هو API الرئيسي للوصول إلى ملفات الوسائط المشتركة (الصور والفيديو والصوت) في Scoped Storage. لا يعمل التطبيق مع مسارات الملفات، بل مع content URIs المقدمة من مزود MediaStore.

ينقسم 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() لن تعطي مسارًا مباشرًا للملف على Android 11+. بدلاً من ذلك، استخدم 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

        // Take persistent URI permission
        val flags = Intent.FLAG_GRANT_READ_URI_PERMISSION or
                       Intent.FLAG_GRANT_WRITE_URI_PERMISSION
        contentResolver.takePersistableUriPermission(treeUri, flags)
    }
}

يمنح SAF التطبيق حق الوصول إلى الدليل الذي اختاره المستخدم حتى إعادة تشغيل الجهاز (عند استخدام takePersistableUriPermission). هذه هي الطريقة الوحيدة للحصول على حق الوصول إلى الملفات العشوائية في التخزين المشترك على Android 11+.

ترحيل التطبيق إلى Scoped Storage

يتطلب ترحيل تطبيق Android موجود إلى Scoped Storage تغييرات في عدة مجالات رئيسية. توصي Google بنهج تدريجي مع الاختبار على Android 11+.

استبدال مسارات الملفات بـ 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 (بدون تغيير)

بعد الترحيل، اختبر التطبيق على Android 11+ (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)، يعمل أكثر من 78% من أجهزة Android النشطة على Android 10+ مع Scoped Storage. الترحيل خطوة إلزامية لنشر التحديثات على Google Play: يجب أن تستهدف التطبيقات الجديدة API 31+، والتحديثات — API 30+.

الأسئلة الشائعة

هل يمكن تعطيل Scoped Storage في Android 11؟

لا، بدءًا من Android 11 (API 30) أصبح Scoped Storage إلزاميًا لجميع التطبيقات. علامة requestLegacyExternalStorage، المتاحة في Android 10، لا تعمل على API 30+. الطريقة الوحيدة للحصول على وصول واسع لنظام الملفات هي إذن MANAGE_EXTERNAL_STORAGE، ولكنه مخصص فقط لمديري الملفات ومضادات الفيروسات.

ما هو MANAGE_EXTERNAL_STORAGE وكيف يتم الحصول عليه؟

MANAGE_EXTERNAL_STORAGE هو إذن خاص للتطبيقات التي تحتاج إلى وصول كامل لنظام الملفات (مديري الملفات، أدوات النسخ الاحتياطي). يفتح طلب الإذن شاشة نظام مع تحذير للمستخدم. عند النشر على Google Play، يجب ملء نموذج إعلان حول ضرورة هذا الإذن.

كيفية الحصول على مسار ملف من content URI في Scoped Storage؟

في Scoped Storage على Android 11+ لا يمكن الحصول على مسار ملف مباشر من 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. ومع ذلك، على Android 10، يمكن تعطيل Scoped Storage عبر requestLegacyExternalStorage. على Android 11+، تحقق مما إذا كانت علامة البيان requestLegacyExternalStorage مضبوطة — إذا لم تكن كذلك، فإن Scoped Storage نشط.

الخلاصة

  • Scoped Storage هو نموذج للوصول إلى الملفات في Android 10+ يقيد القراءة العشوائية للتخزين المشترك.
  • التطبيق لديه وصول كامل إلى بيئته المعزولة الخاصة دون أذونات إضافية.
  • للوصول إلى ملفات الوسائط المشتركة يتم استخدام MediaStore API مع content URIs.
  • للوصول إلى الملفات والدلائل العشوائية يتم استخدام Storage Access Framework (SAF).
  • بدءًا من Android 11، Scoped Storage إلزامي لجميع التطبيقات.
  • أذونات READ_EXTERNAL_STORAGE و WRITE_EXTERNAL_STORAGE لا تعمل في معظم السيناريوهات على Android 11+.
  • عند الترحيل، استبدل مسارات الملفات المباشرة بـ ContentResolver.openInputStream / openOutputStream.

سنقوم بتطوير تطبيق جوال جاهز

تقدم IT Sectr تطبيقات iOS وAndroid للشركات الناشئة والشركات منذ عام 2017. سوف نقدم لك النصح ونقترح أفضل حل.

مناقشة المشروع

اقرأ أيضًا