الوصول إلى ملفات الوسائط من مساحة التخزين المشتركة

لتوفير تجربة أكثر ثراءً للمستخدم، تتيح العديد من التطبيقات للمستخدمين المساهمة والوصول إلى الوسائط المتوفرة على وحدة تخزين خارجية. إطار العمل يوفّر فهرسًا محسَّنًا لمجموعات الوسائط، يُسمى متجر الوسائط، يتيح للمستخدمين استرداد ملفات الوسائط هذه وتعديلها بسهولة أكبر. بالتساوي بعد إلغاء تثبيت تطبيقك، تبقى هذه الملفات على جهاز المستخدم.

أداة اختيار الصور

كبديل لاستخدام متجر الوسائط، يمكن استخدام أداة "اختيار الصور" من Android توفّر طريقة آمنة ومدمجة للمستخدمين لاختيار ملفات الوسائط بدون الحاجة إلى لمنح تطبيقك إذن الوصول إلى مكتبة الوسائط بأكملها هذا الخيار متاح فقط. على الأجهزة المتوافقة. لمزيد من المعلومات، يُرجى الاطّلاع على دليل أداة اختيار الصور.

متجر وسائط

للتفاعل مع التجريد في متجر الوسائط، استخدم ContentResolver العنصر الذي من سياق التطبيق:

Kotlin

val projection = arrayOf(media-database-columns-to-retrieve)
val selection = sql-where-clause-with-placeholder-variables
val selectionArgs = values-of-placeholder-variables
val sortOrder = sql-order-by-clause

applicationContext.contentResolver.query(
    MediaStore.media-type.Media.EXTERNAL_CONTENT_URI,
    projection,
    selection,
    selectionArgs,
    sortOrder
)?.use { cursor ->
    while (cursor.moveToNext()) {
        // Use an ID column from the projection to get
        // a URI representing the media item itself.
    }
}

Java

String[] projection = new String[] {
        media-database-columns-to-retrieve
};
String selection = sql-where-clause-with-placeholder-variables;
String[] selectionArgs = new String[] {
        values-of-placeholder-variables
};
String sortOrder = sql-order-by-clause;

Cursor cursor = getApplicationContext().getContentResolver().query(
    MediaStore.media-type.Media.EXTERNAL_CONTENT_URI,
    projection,
    selection,
    selectionArgs,
    sortOrder
);

while (cursor.moveToNext()) {
    // Use an ID column from the projection to get
    // a URI representing the media item itself.
}

يفحص النظام تلقائيًا مستوى صوت وحدة التخزين الخارجية ويضيف ملفات الوسائط. إلى المجموعات المحددة جيدًا التالية:

  • الصور، بما في ذلك الصور الفوتوغرافية ولقطات الشاشة المحفوظة في دليل DCIM/ وPictures/ يضيف النظام هذه الملفات إلى جدول MediaStore.Images.
  • الفيديوهات المخزَّنة في DCIM/ وMovies/ وPictures/ الأخرى. يضيف النظام هذه الملفات إلى جدول MediaStore.Video.
  • الملفات الصوتية المخزَّنة في Alarms/ وAudiobooks/ وMusic/ أدلة Notifications/ وPodcasts/ وRingtones/ بالإضافة إلى ذلك، يتعرّف نظام التشغيل على قوائم التشغيل الصوتية في Music/ أو Movies/. بالإضافة إلى التسجيلات الصوتية الموجودة في Recordings/ الدليل. يضيف النظام هذه الملفات إلى جدول MediaStore.Audio. لا يتوفّر دليل Recordings/ على نظام التشغيل Android 11 (المستوى 30 لواجهة برمجة التطبيقات). أقل.
  • الملفات التي تم تنزيلها، المخزَّنة في دليل Download/ مشغَّلة الأجهزة التي تعمل بنظام التشغيل Android 10 (المستوى 29 لواجهة برمجة التطبيقات) والإصدارات الأحدث، يتم تخزين هذه الملفات في MediaStore.Downloads المؤقت. لا يتوفّر هذا الجدول على أجهزة Android 9 (المستوى 28 من واجهة برمجة التطبيقات) والإصدارات الأقدم.

يتضمن متجر الوسائط أيضًا مجموعة تسمى MediaStore.Files محتوياته تعتمد على ما إذا كان تطبيقك يستخدم السمة نطاق مساحة التخزين، متوفّرة في التطبيقات التي تستهدف Android 10 أو إصدار أحدث

  • في حال تفعيل ميزة "التخزين الفرعي"، لا تعرض المجموعة سوى الصور والفيديوهات والملفات الصوتية التي أنشأها تطبيقك. لا يحتاج معظم المطوّرين إلى استخدام MediaStore.Files لعرض ملفات الوسائط من التطبيقات الأخرى، ولكن إذا كان لديك مطلبًا محددًا للقيام بذلك، يمكنك الإفصاح عن READ_EXTERNAL_STORAGE إذن. ومع ذلك، ننصحك باستخدام السمة MediaStore واجهات برمجة التطبيقات المخصصة لفتح الملفات التي لم ينشئها تطبيقك.
  • في حال كانت مساحة التخزين غير متاحة أو غير مُستخدَمة، تعرض المجموعة كل أنواعًا من ملفات الوسائط.

طلب الأذونات اللازمة

قبل إجراء عمليات على ملفات الوسائط، تأكَّد من أنّ التطبيق قد أعلن عن الأذونات التي يحتاج إليها للوصول إلى هذه الملفات. ومع ذلك، احرص على عدم الإفصاح عن الأذونات التي لا يحتاج إليها تطبيقك أو لا يستخدمها

أذونات مساحة التخزين

إنّ إمكانية وصول تطبيقك إلى مساحة التخزين تعتمد على ما إذا كان يحتاج إلى أذونات. ملفات الوسائط الخاصة بها أو الملفات التي أنشأتها تطبيقات أخرى

الوصول إلى ملفات الوسائط الخاصة بك

على أجهزة Android 10 أو الإصدارات الأحدث، لا تحتاج إلى الأذونات المتعلقة بمساحة التخزين للوصول إلى ملفات الوسائط التي يمتلكها تطبيقك، بما في ذلك الملفات في MediaStore.Downloads الأولية. إذا كنت تطوّر تطبيقًا للكاميرا، مثلاً، لن تحتاج إلى طلب الأذونات المتعلقة بالسعة التخزينية للوصول إلى الصور التي يتم التقاطها، نظرًا لأن الصور التي تكتبها في متجر الوسائط.

الوصول إلى التطبيقات الأخرى ملفات وسائط

للوصول إلى ملفات الوسائط التي تنشئها تطبيقات أخرى، عليك الإفصاح عن الأذونات المناسبة ذات الصلة بمساحة التخزين، ويجب أن تتواجد الملفات في إحدى مجموعات الوسائط التالية:

وطالما أنّ الملف قابل للعرض من خلال "MediaStore.ImagesMediaStore.Video، أو MediaStore.Audio، يمكن أيضًا عرضها باستخدام MediaStore.Files

يوضح مقتطف الرمز التالي كيفية تحديد مساحة التخزين المناسبة. الأذونات:

<!-- Required only if your app needs to access images or photos
     that other apps created. -->
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />

<!-- Required only if your app needs to access videos
     that other apps created. -->
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />

<!-- Required only if your app needs to access audio files
     that other apps created. -->
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
                 android:maxSdkVersion="29" />

يجب الحصول على أذونات إضافية للتطبيقات التي تعمل على الأجهزة القديمة

إذا تم استخدام تطبيقك على جهاز يعمل بالإصدار 9 من نظام التشغيل Android أو إصدار أقدم، أو إذا أوقف تطبيقك مؤقتًا التخزين، عليك اطلب READ_EXTERNAL_STORAGE إذنًا للوصول إلى أي ملف وسائط. إذا أردت تعديل ملفات الوسائط، يجب اطلب WRITE_EXTERNAL_STORAGE الإذن أيضًا.

يجب توفّر "إطار عمل الوصول إلى مساحة التخزين" للوصول إلى التطبيقات الأخرى. عمليات التنزيل

إذا كان تطبيقك يريد الوصول إلى ملف ضمن مجموعة MediaStore.Downloads التي لم ينشئها التطبيق، يجب استخدام "إطار عمل الوصول إلى مساحة التخزين". للتعلّم تعرّف على مزيد من المعلومات حول كيفية استخدام إطار العمل هذا، راجِع الوصول إلى المستندات والملفات الأخرى من مساحة تخزين مشتركة.

إذن تحديد الموقع الجغرافي للوسائط

إذا كان تطبيقك يستهدف الإصدار 10 من نظام التشغيل Android (المستوى 29 من واجهة برمجة التطبيقات) أو إصدارًا أحدث ويحتاج إلى لاسترداد بيانات EXIF الوصفية غير المنقحة من الصور، يجب أن توضح ACCESS_MEDIA_LOCATION إذن في ملف بيان التطبيق، ثم طلب هذا الإذن في وقت التشغيل.

البحث عن تحديثات في متجر الوسائط

للوصول إلى ملفات الوسائط بطريقة أكثر موثوقية، لا سيما إذا كان التطبيق يخزِّن معرّفات الموارد المنتظمة (URI) أو البيانات من تخزين الوسائط، تحقق مما إذا كان إصدار متجر الوسائط قد تغير مقارنةً بآخر مرة أجريت فيها مزامنة بيانات الوسائط. لإجراء هذا الفحص تحديثات، اتصال getVersion() الإصدار الذي يتم عرضه هو سلسلة فريدة تتغير كلما تم تخزين الوسائط تغييرات كبيرة. إذا كان الإصدار المعروض مختلفًا عن آخر نسخة تمت مزامنتها الإصدار ثم إعادة فحص ذاكرة التخزين المؤقت للوسائط في تطبيقك وإعادة مزامنتها.

أكمِل هذا الفحص عند بدء تشغيل عملية التطبيق. ليست هناك حاجة إلى التحقق من كل مرة تستعلم فيها من مخزن الوسائط.

ولا تفترض أي تفاصيل تنفيذية في ما يتعلق برقم الإصدار.

الاستعلام عن مجموعة وسائط

للعثور على وسائط تتوافق مع مجموعة معينة من الشروط، مثل المدة لمدة 5 دقائق أو أكثر، استخدم عبارة تحديد تشبه SQL مشابهة لتلك كما هو موضح في مقتطف الرمز التالي:

Kotlin

// Need the READ_EXTERNAL_STORAGE permission if accessing video files that your
// app didn't create.

// Container for information about each video.
data class Video(val uri: Uri,
    val name: String,
    val duration: Int,
    val size: Int
)
val videoList = mutableListOf<Video>()

val collection =
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
        MediaStore.Video.Media.getContentUri(
            MediaStore.VOLUME_EXTERNAL
        )
    } else {
        MediaStore.Video.Media.EXTERNAL_CONTENT_URI
    }

val projection = arrayOf(
    MediaStore.Video.Media._ID,
    MediaStore.Video.Media.DISPLAY_NAME,
    MediaStore.Video.Media.DURATION,
    MediaStore.Video.Media.SIZE
)

// Show only videos that are at least 5 minutes in duration.
val selection = "${MediaStore.Video.Media.DURATION} >= ?"
val selectionArgs = arrayOf(
    TimeUnit.MILLISECONDS.convert(5, TimeUnit.MINUTES).toString()
)

// Display videos in alphabetical order based on their display name.
val sortOrder = "${MediaStore.Video.Media.DISPLAY_NAME} ASC"

val query = ContentResolver.query(
    collection,
    projection,
    selection,
    selectionArgs,
    sortOrder
)
query?.use { cursor ->
    // Cache column indices.
    val idColumn = cursor.getColumnIndexOrThrow(MediaStore.Video.Media._ID)
    val nameColumn =
            cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DISPLAY_NAME)
    val durationColumn =
            cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DURATION)
    val sizeColumn = cursor.getColumnIndexOrThrow(MediaStore.Video.Media.SIZE)

    while (cursor.moveToNext()) {
        // Get values of columns for a given video.
        val id = cursor.getLong(idColumn)
        val name = cursor.getString(nameColumn)
        val duration = cursor.getInt(durationColumn)
        val size = cursor.getInt(sizeColumn)

        val contentUri: Uri = ContentUris.withAppendedId(
            MediaStore.Video.Media.EXTERNAL_CONTENT_URI,
            id
        )

        // Stores column values and the contentUri in a local object
        // that represents the media file.
        videoList += Video(contentUri, name, duration, size)
    }
}

Java

// Need the READ_EXTERNAL_STORAGE permission if accessing video files that your
// app didn't create.

// Container for information about each video.
class Video {
    private final Uri uri;
    private final String name;
    private final int duration;
    private final int size;

    public Video(Uri uri, String name, int duration, int size) {
        this.uri = uri;
        this.name = name;
        this.duration = duration;
        this.size = size;
    }
}
List<Video> videoList = new ArrayList<Video>();

Uri collection;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
    collection = MediaStore.Video.Media.getContentUri(MediaStore.VOLUME_EXTERNAL);
} else {
    collection = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
}

String[] projection = new String[] {
    MediaStore.Video.Media._ID,
    MediaStore.Video.Media.DISPLAY_NAME,
    MediaStore.Video.Media.DURATION,
    MediaStore.Video.Media.SIZE
};
String selection = MediaStore.Video.Media.DURATION +
        " >= ?";
String[] selectionArgs = new String[] {
    String.valueOf(TimeUnit.MILLISECONDS.convert(5, TimeUnit.MINUTES));
};
String sortOrder = MediaStore.Video.Media.DISPLAY_NAME + " ASC";

try (Cursor cursor = getApplicationContext().getContentResolver().query(
    collection,
    projection,
    selection,
    selectionArgs,
    sortOrder
)) {
    // Cache column indices.
    int idColumn = cursor.getColumnIndexOrThrow(MediaStore.Video.Media._ID);
    int nameColumn =
            cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DISPLAY_NAME);
    int durationColumn =
            cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DURATION);
    int sizeColumn = cursor.getColumnIndexOrThrow(MediaStore.Video.Media.SIZE);

    while (cursor.moveToNext()) {
        // Get values of columns for a given video.
        long id = cursor.getLong(idColumn);
        String name = cursor.getString(nameColumn);
        int duration = cursor.getInt(durationColumn);
        int size = cursor.getInt(sizeColumn);

        Uri contentUri = ContentUris.withAppendedId(
                MediaStore.Video.Media.EXTERNAL_CONTENT_URI, id);

        // Stores column values and the contentUri in a local object
        // that represents the media file.
        videoList.add(new Video(contentUri, name, duration, size));
    }
}

عند إجراء طلب بحث كهذا في تطبيقك، يجب مراعاة ما يلي:

  • وعليك استدعاء الطريقة query() في سلسلة تعليمات عامل التشغيل.
  • خزّن فهارس الأعمدة في ذاكرة التخزين المؤقت حتى لا تحتاج إلى استدعاء getColumnIndexOrThrow() في كل مرة تقوم فيها بمعالجة صف من نتيجة الاستعلام.
  • أضِف المعرّف إلى معرّف الموارد المنتظم (URI) للمحتوى كما هو موضّح في هذا المثال.
  • تتطلّب الأجهزة التي تعمل بنظام التشغيل Android 10 والإصدارات الأحدث عمودًا الأسماء التي تم تحديدها في واجهة برمجة تطبيقات MediaStore. إذا كانت هناك مكتبة تابعة داخل تطبيقك تتوقع ظهور عمود يستخدم اسمًا غير معرَّف في واجهة برمجة التطبيقات، مثل "MimeType"، CursorWrapper للانتقال ديناميكيًا لترجمة اسم العمود في عملية تطبيقك.

تحميل صور مصغرة للملفات

إذا كان تطبيقك يعرض عدة ملفات وسائط ويطلب من المستخدم اختيار أحدها من هذه الملفات، يصبح تحميل معاينة النُسخ أو الصور المصغّرة من الملفات بدلاً من الملفات نفسها.

لتحميل الصورة المصغّرة لملف وسائط معين، استخدم loadThumbnail() ونمرر حجم الصورة المصغرة التي تريد تحميلها، كما هو موضح في مقتطف الرمز التالي:

Kotlin

// Load thumbnail of a specific media item.
val thumbnail: Bitmap =
        applicationContext.contentResolver.loadThumbnail(
        content-uri, Size(640, 480), null)

Java

// Load thumbnail of a specific media item.
Bitmap thumbnail =
        getApplicationContext().getContentResolver().loadThumbnail(
        content-uri, new Size(640, 480), null);

فتح ملف وسائط

يعتمد المنطق المحدد الذي تستخدمه لفتح ملف وسائط على ما إذا كان وأفضل تمثيل لمحتوى الوسائط هو واصف ملف أو تدفق ملف أو مسار الملف المباشر.

أداة وصف الملف

لفتح ملف وسائط باستخدام واصف ملف، استخدِم منطقًا مشابهًا لما يظهر في مقتطف الرمز التالي:

Kotlin

// Open a specific media item using ParcelFileDescriptor.
val resolver = applicationContext.contentResolver

// "rw" for read-and-write.
// "rwt" for truncating or overwriting existing file contents.
val readOnlyMode = "r"
resolver.openFileDescriptor(content-uri, readOnlyMode).use { pfd ->
    // Perform operations on "pfd".
}

Java

// Open a specific media item using ParcelFileDescriptor.
ContentResolver resolver = getApplicationContext()
        .getContentResolver();

// "rw" for read-and-write.
// "rwt" for truncating or overwriting existing file contents.
String readOnlyMode = "r";
try (ParcelFileDescriptor pfd =
        resolver.openFileDescriptor(content-uri, readOnlyMode)) {
    // Perform operations on "pfd".
} catch (IOException e) {
    e.printStackTrace();
}

بث الملفات

لفتح ملف وسائط باستخدام تدفق ملفات، استخدِم منطقًا مشابهًا لما يظهر في مقتطف الرمز التالي:

Kotlin

// Open a specific media item using InputStream.
val resolver = applicationContext.contentResolver
resolver.openInputStream(content-uri).use { stream ->
    // Perform operations on "stream".
}

Java

// Open a specific media item using InputStream.
ContentResolver resolver = getApplicationContext()
        .getContentResolver();
try (InputStream stream = resolver.openInputStream(content-uri)) {
    // Perform operations on "stream".
}

مسارات الملفات المباشرة

لمساعدة تطبيقك في العمل بسلاسة أكبر باستخدام مكتبات وسائط تابعة لجهات خارجية، يُرجى اتّباع الخطوات التالية: يتيح لك Android 11 (المستوى 30) والإصدارات الأحدث استخدام واجهات برمجة تطبيقات بخلاف MediaStore واجهة برمجة التطبيقات للوصول إليها ملفات الوسائط من مساحة التخزين المشتركة. يمكنك بدلاً من ذلك الوصول إلى ملفات الوسائط مباشرةً. باستخدام أي من واجهات برمجة التطبيقات التالية:

  • واجهة برمجة تطبيقات File
  • المكتبات الأصلية، مثل fopen()

إذا لم يكن لديك أي أذونات ذات صلة بمساحة التخزين، يمكنك الوصول إلى الملفات في الدليل الخاص بالتطبيق بالإضافة إلى الوسائط الملفات المنسوبة إلى تطبيقك باستخدام File API.

إذا حاول تطبيقك الوصول إلى ملف باستخدام واجهة برمجة تطبيقات File ولم يكن يحتوي على الأذونات اللازمة، FileNotFoundException يحدث.

للوصول إلى الملفات الأخرى في مساحة التخزين المشتركة على جهاز يعمل بنظام التشغيل Android 10 (واجهة برمجة التطبيقات). المستوى 29)، ننصحك بإيقافه مؤقتًا التخزين عن طريق ضبط requestLegacyExternalStorage إلى true في ملف البيان لتطبيقك. للوصول إلى ملفات الوسائط باستخدام الملفات الأصلية على Android 10، يجب أيضًا طلب READ_EXTERNAL_STORAGE إذن.

الاعتبارات الواجب مراعاتها عند الوصول إلى محتوى الوسائط

عند الوصول إلى محتوى وسائط، يرجى أخذ الاعتبارات التي تمت مناقشتها في الأقسام التالية.

البيانات المؤقتة

إذا كان تطبيقك يخزِّن معرّفات الموارد المنتظمة (URI) أو البيانات من متجر الوسائط، ابحث بشكل دوري عن تحديثات إلى متجر الوسائط. يتيح هذا التحقق تبقى البيانات المخزنة مؤقتًا من جهة التطبيق متزامنة مع بيانات موفّر النظام من جهة النظام.

الأداء

عند إجراء قراءات متسلسلة لملفات الوسائط باستخدام مسارات الملفات المباشرة، الأداء يمكن مقارنته بأداء MediaStore API.

عندما تجري عمليات قراءة وكتابية عشوائية لملفات الوسائط باستخدام مسارات الملفات المباشرة، إلا أن هذه العملية قد تصل إلى ضعف البطء. في هذه المواقف، ننصحك باستخدام واجهة برمجة تطبيقات MediaStore بدلاً من ذلك.

عمود البيانات

عند الوصول إلى ملف وسائط حالي، يمكنك استخدام قيمة DATA في منطقك. وذلك لأن هذه القيمة لها مسار ملف صالح. ومع ذلك، لا تفعل أن الملف متاح دائمًا. كن مستعدًا للتعامل مع أي ملفات تعتمد على أخطاء I/O التي تحدث.

لإنشاء ملف وسائط أو تحديثه، لا تستخدم قيمة عمود "DATA". وبدلاً من ذلك، استخدِم قيم DISPLAY_NAME أو RELATIVE_PATH والأعمدة.

أحجام التخزين

يمكن للتطبيقات التي تستهدف الإصدار 10 من نظام التشغيل Android أو الإصدارات الأحدث الوصول إلى الاسم الفريد الذي يخصصه النظام لكل وحدة تخزين خارجية نظام التسمية هذا في تنظيم المحتوى وفهرسته بكفاءة، والتحكم في حيث يتم تخزين ملفات الوسائط الجديدة.

تُعد المجلدات التالية من المفيد وضعها في الاعتبار على وجه الخصوص:

  • تشير رسالة الأشكال البيانية VOLUME_EXTERNAL يتيح مستوى الصوت عرض كل وحدات التخزين المشتركة على الجهاز. يمكنك قراءة محتوى هذا الجزء الاصطناعي، ولكن لا يمكنك تعديله.
  • تشير رسالة الأشكال البيانية VOLUME_EXTERNAL_PRIMARY الحجم الحجم الأساسي لمساحة التخزين المشتركة على الجهاز. يمكنك قراءة محتوى هذا المجلد وتعديله.

يمكنك استكشاف مجلدات أخرى من خلال الاتصال MediaStore.getExternalVolumeNames():

Kotlin

val volumeNames: Set<String> = MediaStore.getExternalVolumeNames(context)
val firstVolumeName = volumeNames.iterator().next()

Java

Set<String> volumeNames = MediaStore.getExternalVolumeNames(context);
String firstVolumeName = volumeNames.iterator().next();

الموقع الجغرافي الذي تم فيه التقاط الوسائط

تحتوي بعض الصور ومقاطع الفيديو على معلومات الموقع في بياناتها الوصفية، يعرض مكان التقاط الصورة أو مكان تصوير فيديو تسجيل البيانات.

وتعتمد كيفية وصولك إلى معلومات الموقع الجغرافي هذه في تطبيقك على ما إذا كنت الدخول إلى معلومات الموقع لالتقاط صورة فوتوغرافية أو مقطع فيديو.

صور فوتوغرافية

إذا كان تطبيقك يستخدم مساحة تخزين ذات نطاق واسع، سيتم تفعيل يخفي النظام معلومات الموقع تلقائيًا. للوصول إلى هذه المعلومات، أكمل الخطوات التالية:

  1. اطلب ACCESS_MEDIA_LOCATION الإذن في بيان التطبيق.
  2. من الكائن MediaStore، يمكنك الحصول على وحدات البايت الدقيقة للصورة من خلال يتصل setRequireOriginal() وتمرير عنوان URI للصورة، كما هو موضح في مقتطف الرمز التالي:

    Kotlin

    val photoUri: Uri = Uri.withAppendedPath(
            MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
            cursor.getString(idColumnIndex)
    )
    
    // Get location data using the Exifinterface library.
    // Exception occurs if ACCESS_MEDIA_LOCATION permission isn't granted.
    photoUri = MediaStore.setRequireOriginal(photoUri)
    contentResolver.openInputStream(photoUri)?.use { stream ->
        ExifInterface(stream).run {
            // If lat/long is null, fall back to the coordinates (0, 0).
            val latLong = latLong ?: doubleArrayOf(0.0, 0.0)
        }
    }
    

    Java

    Uri photoUri = Uri.withAppendedPath(
            MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
            cursor.getString(idColumnIndex));
    
    final double[] latLong;
    
    // Get location data using the Exifinterface library.
    // Exception occurs if ACCESS_MEDIA_LOCATION permission isn't granted.
    photoUri = MediaStore.setRequireOriginal(photoUri);
    InputStream stream = getContentResolver().openInputStream(photoUri);
    if (stream != null) {
        ExifInterface exifInterface = new ExifInterface(stream);
        double[] returnedLatLong = exifInterface.getLatLong();
    
        // If lat/long is null, fall back to the coordinates (0, 0).
        latLong = returnedLatLong != null ? returnedLatLong : new double[2];
    
        // Don't reuse the stream associated with
        // the instance of "ExifInterface".
        stream.close();
    } else {
        // Failed to load the stream, so return the coordinates (0, 0).
        latLong = new double[2];
    }
    

الفيديوهات

للوصول إلى معلومات الموقع ضمن البيانات الوصفية للفيديو، استخدم MediaMetadataRetriever كما هو موضح في مقتطف الرمز التالي. لا يحتاج التطبيق إلى طلب أي أذونات إضافية لاستخدام هذا الصف.

Kotlin

val retriever = MediaMetadataRetriever()
val context = applicationContext

// Find the videos that are stored on a device by querying the video collection.
val query = ContentResolver.query(
    collection,
    projection,
    selection,
    selectionArgs,
    sortOrder
)
query?.use { cursor ->
    val idColumn = cursor.getColumnIndexOrThrow(MediaStore.Video.Media._ID)
    while (cursor.moveToNext()) {
        val id = cursor.getLong(idColumn)
        val videoUri: Uri = ContentUris.withAppendedId(
            MediaStore.Video.Media.EXTERNAL_CONTENT_URI,
            id
        )
        extractVideoLocationInfo(videoUri)
    }
}

private fun extractVideoLocationInfo(videoUri: Uri) {
    try {
        retriever.setDataSource(context, videoUri)
    } catch (e: RuntimeException) {
        Log.e(APP_TAG, "Cannot retrieve video file", e)
    }
    // Metadata uses a standardized format.
    val locationMetadata: String? =
            retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_LOCATION)
}

Java

MediaMetadataRetriever retriever = new MediaMetadataRetriever();
Context context = getApplicationContext();

// Find the videos that are stored on a device by querying the video collection.
try (Cursor cursor = context.getContentResolver().query(
    collection,
    projection,
    selection,
    selectionArgs,
    sortOrder
)) {
    int idColumn = cursor.getColumnIndexOrThrow(MediaStore.Video.Media._ID);
    while (cursor.moveToNext()) {
        long id = cursor.getLong(idColumn);
        Uri videoUri = ContentUris.withAppendedId(
                MediaStore.Video.Media.EXTERNAL_CONTENT_URI, id);
        extractVideoLocationInfo(videoUri);
    }
}

private void extractVideoLocationInfo(Uri videoUri) {
    try {
        retriever.setDataSource(context, videoUri);
    } catch (RuntimeException e) {
        Log.e(APP_TAG, "Cannot retrieve video file", e);
    }
    // Metadata uses a standardized format.
    String locationMetadata = retriever.extractMetadata(
            MediaMetadataRetriever.METADATA_KEY_LOCATION);
}

المشاركة

تتيح بعض التطبيقات للمستخدمين مشاركة ملفات الوسائط مع بعضهم البعض. على سبيل المثال، يمكن التواصل تطبيقات الوسائط للمستخدمين مشاركة الصور والفيديوهات مع الأصدقاء.

لمشاركة ملفات الوسائط، استخدِم معرّف الموارد المنتظم (URI) content:// على النحو الموصى به في الدليل إنشاء موفّر محتوى

إحالة ملفات الوسائط إلى التطبيق

عند تفعيل التخزين الفرعي تطبيق يستهدف الإصدار 10 من نظام التشغيل Android أو الإصدارات الأحدث، فإن النظام ينسب التطبيق على كل ملف وسائط، وهو ما يحدد الملفات التي يمكن للتطبيق الوصول إليها لم يطلب أيّ أذونات لمساحة التخزين. يمكن إحالة كل ملف إلى في تطبيق واحد. لذلك، إذا أنشأ تطبيقك ملف وسائط مخزَّنًا في الصور أو الفيديوهات أو الملفات الصوتية، يمكن لتطبيقك الوصول إلى الملف.

ومع ذلك، إذا ألغى المستخدم تثبيت تطبيقك وأعاد تثبيته، عليك طلب ذلك. READ_EXTERNAL_STORAGE للوصول إلى الملفات التي أنشأها تطبيقك في الأصل. طلب الإذن هذا مطلوب لأن النظام يعتبر أن الملف منسوب إلى الإصدار المثبت مسبقًا من التطبيق، بدلاً من الإصدار المثبت حديثًا.

إضافة عنصر

لإضافة عنصر وسائط إلى مجموعة حالية، يمكنك استخدام رمز مشابه المتابعة. يصل مقتطف الرمز هذا إلى مستوى VOLUME_EXTERNAL_PRIMARY. على الأجهزة التي تعمل بنظام التشغيل Android 10 أو الإصدارات الأحدث. وهذا لأنه على هذه الأجهزة، يمكن فقط تعديل محتوى أي وحدة تخزين إذا كان مستوى الصوت الأساسي، كما هو موضَّح في قسم أحجام مساحة التخزين.

Kotlin

// Add a specific media item.
val resolver = applicationContext.contentResolver

// Find all audio files on the primary external storage device.
val audioCollection =
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
        MediaStore.Audio.Media.getContentUri(
            MediaStore.VOLUME_EXTERNAL_PRIMARY
        )
    } else {
        MediaStore.Audio.Media.EXTERNAL_CONTENT_URI
    }

// Publish a new song.
val newSongDetails = ContentValues().apply {
    put(MediaStore.Audio.Media.DISPLAY_NAME, "My Song.mp3")
}

// Keep a handle to the new song's URI in case you need to modify it
// later.
val myFavoriteSongUri = resolver
        .insert(audioCollection, newSongDetails)

Java

// Add a specific media item.
ContentResolver resolver = getApplicationContext()
        .getContentResolver();

// Find all audio files on the primary external storage device.
Uri audioCollection;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
    audioCollection = MediaStore.Audio.Media
            .getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY);
} else {
    audioCollection = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
}

// Publish a new song.
ContentValues newSongDetails = new ContentValues();
newSongDetails.put(MediaStore.Audio.Media.DISPLAY_NAME,
        "My Song.mp3");

// Keep a handle to the new song's URI in case you need to modify it
// later.
Uri myFavoriteSongUri = resolver
        .insert(audioCollection, newSongDetails);

تبديل حالة "معلّق" لملفات الوسائط

إذا كان تطبيقك يُجري عمليات قد تستغرق وقتًا طويلاً، مثل الكتابة إلى ملفات الوسائط، فمن المفيد أن يكون لديك حق وصول حصري إلى الملف لأنه يجري ومعالجتها. على الأجهزة التي تعمل بنظام التشغيل Android 10 أو الإصدارات الأحدث، يمكن لتطبيقك الحصول على هذا الوصول الحصري من خلال تحديد قيمة IS_PENDING علم على 1. يمكن لتطبيقك فقط عرض الملف إلى أن يغيّر التطبيق قيمة IS_PENDING رجوع إلى 0.

يستنِد مقتطف الرمز التالي إلى مقتطف الرمز السابق. هذا النمط مقتطف يوضح كيفية استخدام علامة IS_PENDING عند تخزين أغنية طويلة في الدليل المقابل لمجموعة MediaStore.Audio:

Kotlin

// Add a media item that other apps don't see until the item is
// fully written to the media store.
val resolver = applicationContext.contentResolver

// Find all audio files on the primary external storage device.
val audioCollection =
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
        MediaStore.Audio.Media.getContentUri(
            MediaStore.VOLUME_EXTERNAL_PRIMARY
        )
    } else {
        MediaStore.Audio.Media.EXTERNAL_CONTENT_URI
    }

val songDetails = ContentValues().apply {
    put(MediaStore.Audio.Media.DISPLAY_NAME, "My Workout Playlist.mp3")
    put(MediaStore.Audio.Media.IS_PENDING, 1)
}

val songContentUri = resolver.insert(audioCollection, songDetails)

// "w" for write.
resolver.openFileDescriptor(songContentUri, "w", null).use { pfd ->
    // Write data into the pending audio file.
}

// Now that you're finished, release the "pending" status and let other apps
// play the audio track.
songDetails.clear()
songDetails.put(MediaStore.Audio.Media.IS_PENDING, 0)
resolver.update(songContentUri, songDetails, null, null)

Java

// Add a media item that other apps don't see until the item is
// fully written to the media store.
ContentResolver resolver = getApplicationContext()
        .getContentResolver();

// Find all audio files on the primary external storage device.
Uri audioCollection;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
    audioCollection = MediaStore.Audio.Media
            .getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY);
} else {
    audioCollection = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
}

ContentValues songDetails = new ContentValues();
songDetails.put(MediaStore.Audio.Media.DISPLAY_NAME,
        "My Workout Playlist.mp3");
songDetails.put(MediaStore.Audio.Media.IS_PENDING, 1);

Uri songContentUri = resolver
        .insert(audioCollection, songDetails);

// "w" for write.
try (ParcelFileDescriptor pfd =
        resolver.openFileDescriptor(songContentUri, "w", null)) {
    // Write data into the pending audio file.
}

// Now that you're finished, release the "pending" status and let other apps
// play the audio track.
songDetails.clear();
songDetails.put(MediaStore.Audio.Media.IS_PENDING, 0);
resolver.update(songContentUri, songDetails, null, null);

تقديم تلميح حول موقع الملف

عندما يخزِّن تطبيقك الوسائط على جهاز يعمل بنظام التشغيل Android 10، من خلال يتم تنظيم الوسائط افتراضيًا بناءً على نوعها. على سبيل المثال، لا تتوفر ميزات جديدة يتم وضع ملفات الصور في Environment.DIRECTORY_PICTURES الذي يتجاوب مع دليل مجموعة MediaStore.Images

إذا كان التطبيق على علم بموقع معين يمكن تخزين الملفات فيه، مثل كألبوم صور يسمى Pictures/MyVacationPictures، يمكنك ضبط MediaColumns.RELATIVE_PATH لتزويد النظام بتلميح حول مكان تخزين الملفات المكتوبة حديثًا.

تعديل عنصر

لتعديل ملف وسائط يملكه تطبيقك، استخدِم رمزًا برمجيًا مشابهًا لما يلي:

Kotlin

// Updates an existing media item.
val mediaId = // MediaStore.Audio.Media._ID of item to update.
val resolver = applicationContext.contentResolver

// When performing a single item update, prefer using the ID.
val selection = "${MediaStore.Audio.Media._ID} = ?"

// By using selection + args you protect against improper escaping of // values.
val selectionArgs = arrayOf(mediaId.toString())

// Update an existing song.
val updatedSongDetails = ContentValues().apply {
    put(MediaStore.Audio.Media.DISPLAY_NAME, "My Favorite Song.mp3")
}

// Use the individual song's URI to represent the collection that's
// updated.
val numSongsUpdated = resolver.update(
        myFavoriteSongUri,
        updatedSongDetails,
        selection,
        selectionArgs)

Java

// Updates an existing media item.
long mediaId = // MediaStore.Audio.Media._ID of item to update.
ContentResolver resolver = getApplicationContext()
        .getContentResolver();

// When performing a single item update, prefer using the ID.
String selection = MediaStore.Audio.Media._ID + " = ?";

// By using selection + args you protect against improper escaping of
// values. Here, "song" is an in-memory object that caches the song's
// information.
String[] selectionArgs = new String[] { getId().toString() };

// Update an existing song.
ContentValues updatedSongDetails = new ContentValues();
updatedSongDetails.put(MediaStore.Audio.Media.DISPLAY_NAME,
        "My Favorite Song.mp3");

// Use the individual song's URI to represent the collection that's
// updated.
int numSongsUpdated = resolver.update(
        myFavoriteSongUri,
        updatedSongDetails,
        selection,
        selectionArgs);

إذا كانت مساحة التخزين غير متاحة أو غير مفعّلة، تظهر العملية في يعمل مقتطف الرمز السابق أيضًا مع الملفات التي لا يمتلكها تطبيقك.

التحديث في الرمز الأصلي

إذا كنت تريد كتابة ملفات وسائط باستخدام مكتبات أصلية، فمرّر واصف الملفات المرتبطة من رمزك المستند إلى Java أو Kotlin في الرمز الأصلي.

يعرض مقتطف الرمز التالي كيفية تمرير واصف ملف كائن الوسائط في الرمز الأصلي لتطبيقك:

Kotlin

val contentUri: Uri = ContentUris.withAppendedId(
        MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
        cursor.getLong(BaseColumns._ID))
val fileOpenMode = "r"
val parcelFd = resolver.openFileDescriptor(contentUri, fileOpenMode)
val fd = parcelFd?.detachFd()
// Pass the integer value "fd" into your native code. Remember to call
// close(2) on the file descriptor when you're done using it.

Java

Uri contentUri = ContentUris.withAppendedId(
        MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
        cursor.getLong(Integer.parseInt(BaseColumns._ID)));
String fileOpenMode = "r";
ParcelFileDescriptor parcelFd =
        resolver.openFileDescriptor(contentUri, fileOpenMode);
if (parcelFd != null) {
    int fd = parcelFd.detachFd();
    // Pass the integer value "fd" into your native code. Remember to call
    // close(2) on the file descriptor when you're done using it.
}

تحديث التطبيقات الأخرى ملفات وسائط

إذا كان تطبيقك يستخدم مساحة تخزين مخصّصة، تحديث ملف وسائط ساهم فيه تطبيق مختلف في تخزين الوسائط.

ومع ذلك، يمكنك الحصول على موافقة المستخدم لتعديل الملف من خلال التقاط RecoverableSecurityException التي تطرحها المنصة. يمكنك بعد ذلك أن تطلب من المستخدم منح تطبيقك إذن الوصول للكتابة إلى هذا العنصر المحدّد، كما هو موضّح في مقتطف الرمز التالي:

Kotlin

// Apply a grayscale filter to the image at the given content URI.
try {
    // "w" for write.
    contentResolver.openFileDescriptor(image-content-uri, "w")?.use {
        setGrayscaleFilter(it)
    }
} catch (securityException: SecurityException) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
        val recoverableSecurityException = securityException as?
            RecoverableSecurityException ?:
            throw RuntimeException(securityException.message, securityException)

        val intentSender =
            recoverableSecurityException.userAction.actionIntent.intentSender
        intentSender?.let {
            startIntentSenderForResult(intentSender, image-request-code,
                    null, 0, 0, 0, null)
        }
    } else {
        throw RuntimeException(securityException.message, securityException)
    }
}

Java

try {
    // "w" for write.
    ParcelFileDescriptor imageFd = getContentResolver()
            .openFileDescriptor(image-content-uri, "w");
    setGrayscaleFilter(imageFd);
} catch (SecurityException securityException) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
        RecoverableSecurityException recoverableSecurityException;
        if (securityException instanceof RecoverableSecurityException) {
            recoverableSecurityException =
                    (RecoverableSecurityException)securityException;
        } else {
            throw new RuntimeException(
                    securityException.getMessage(), securityException);
        }
        IntentSender intentSender =recoverableSecurityException.getUserAction()
                .getActionIntent().getIntentSender();
        startIntentSenderForResult(intentSender, image-request-code,
                null, 0, 0, 0, null);
    } else {
        throw new RuntimeException(
                securityException.getMessage(), securityException);
    }
}

يجب إكمال هذه العملية في كل مرة يحتاج فيها تطبيقك إلى تعديل ملف وسائط لم ينشئوه.

بدلاً من ذلك، إذا كان تطبيقك يعمل بنظام التشغيل Android 11 أو الإصدارات الأحدث، يمكنك: يتم السماح للمستخدمين بمنح تطبيقك إذن الوصول للكتابة إلى مجموعة من ملفات الوسائط. يمكنك استخدام createWriteRequest() كما هو موضح في القسم المتعلق بكيفية إدارة مجموعات من الوسائط الملفات.

إذا كان لتطبيقك حالة استخدام أخرى لا تشملها مساحة التخزين المحدّدة النطاق، يمكنك تقديم طلب ميزة إيقاف النطاق مؤقتًا مساحة التخزين.

إزالة عنصر

لإزالة عنصر لم يعُد تطبيقك بحاجة إليه في متجر الوسائط، استخدِم أسلوب منطقي. مشابه لما هو معروض في مقتطف الرمز التالي:

Kotlin

// Remove a specific media item.
val resolver = applicationContext.contentResolver

// URI of the image to remove.
val imageUri = "..."

// WHERE clause.
val selection = "..."
val selectionArgs = "..."

// Perform the actual removal.
val numImagesRemoved = resolver.delete(
        imageUri,
        selection,
        selectionArgs)

Java

// Remove a specific media item.
ContentResolver resolver = getApplicationContext()
        getContentResolver();

// URI of the image to remove.
Uri imageUri = "...";

// WHERE clause.
String selection = "...";
String[] selectionArgs = "...";

// Perform the actual removal.
int numImagesRemoved = resolver.delete(
        imageUri,
        selection,
        selectionArgs);

إذا كانت مساحة التخزين غير متاحة أو غير مفعّلة، يمكنك استخدام الأقسام السابقة مقتطف الرمز لإزالة الملفات التي تمتلكها التطبيقات الأخرى. في حال تفعيل التخزين الفرعي، ومع ذلك، يجب اكتشاف الخطأ RecoverableSecurityException لكل ملف يريد تطبيقك إزالته، كما هو موضّح في القسم المتعلق بتحديث الوسائط .

إذا كان تطبيقك يعمل بنظام التشغيل Android 11 أو إصدار أحدث، يمكنك السماح للمستخدمين اختَر مجموعة من ملفات الوسائط لإزالتها استخدام createTrashRequest() الطريقة أو createDeleteRequest() كما هو موضح في القسم المتعلق بكيفية إدارة مجموعات من الوسائط .

إذا كان لتطبيقك حالة استخدام أخرى لا تشملها مساحة التخزين المحدّدة النطاق، يمكنك تقديم طلب ميزة إيقاف النطاق مؤقتًا مساحة التخزين.

رصد التحديثات التي تم إجراؤها على ملفات الوسائط

قد يحتاج تطبيقك إلى تحديد وحدات التخزين التي تحتوي على ملفات الوسائط التي تستخدمها هذه التطبيقات. مضافة أو معدلة، مقارنة بأي نقطة زمنية سابقة. لرصد هذه التغييرات بشكل موثوق به، نقل حجم التخزين محل الاهتمام إلى getGeneration() طالما لم يتغير إصدار متجر الوسائط، فستظل القيمة المعروضة تزيد بشكل رتيب بمرور الوقت.

وعلى وجه الخصوص، تُعد getGeneration() أكثر فعالية من التواريخ في أعمدة الوسائط، مثل DATE_ADDED وDATE_MODIFIED. وذلك لأن قيم أعمدة الوسائط هذه يمكن أن تتغير عندما يستدعي التطبيق setLastModified() أو متى يغير المستخدم ساعة النظام.

إدارة مجموعات من ملفات الوسائط

في نظام التشغيل Android 11 والإصدارات الأحدث، يمكنك أن تطلب من المستخدم اختيار مجموعة من ملفات الوسائط، ثم تعديل ملفات الوسائط هذه في عملية واحدة. هذه الطرق توفر اتساقًا أفضل عبر الأجهزة، كما أن الطرق تسهل للمستخدمين لإدارة مجموعاتهم الإعلامية.

الطرق التي توفر هذا "التحديث المجمَّع" الوظائف التالي:

createWriteRequest()
اطلب من المستخدم منح تطبيقك إذنًا بالكتابة في المجموعة المحدّدة من ملفات الوسائط.
createFavoriteRequest()
طلب أن يضع المستخدم علامة على ملفات الوسائط المحددة باعتبارها بعض ملفات "المفضلة" الوسائط على الجهاز. يمكن لأي تطبيق لديه إذن وصول للقراءة إلى هذا الملف ومعرفة أنّ المستخدم وضع علامة "مفضّل" على الملف.
createTrashRequest()

اطلب من المستخدم وضع ملفات الوسائط المحدّدة في مهملات الجهاز. يتم حذف العناصر نهائيًا من المهملات بعد وقت يحدّده النظام. الفترة.

createDeleteRequest()

طلب أن يحذف المستخدم ملفات الوسائط المحدَّدة نهائيًا فورًا، دون وضعها في المهملات مسبقًا.

بعد استدعاء أي من هذه الطرق، ينشئ النظام PendingIntent. بعد التطبيق يستدعي هذا الغرض، سيظهر للمستخدمين مربع حوار يطلب موافقتهم على تطبيقك لتعديل ملفات الوسائط المحدّدة أو حذفها

على سبيل المثال، إليك كيفية تنظيم مكالمة إلى createWriteRequest():

Kotlin

val urisToModify = /* A collection of content URIs to modify. */
val editPendingIntent = MediaStore.createWriteRequest(contentResolver,
        urisToModify)

// Launch a system prompt requesting user permission for the operation.
startIntentSenderForResult(editPendingIntent.intentSender, EDIT_REQUEST_CODE,
    null, 0, 0, 0)

Java

List<Uri> urisToModify = /* A collection of content URIs to modify. */
PendingIntent editPendingIntent = MediaStore.createWriteRequest(contentResolver,
                  urisToModify);

// Launch a system prompt requesting user permission for the operation.
startIntentSenderForResult(editPendingIntent.getIntentSender(),
    EDIT_REQUEST_CODE, null, 0, 0, 0);

تقييم رد المستخدم إذا قدّم المستخدم موافقته، يمكنك تشغيل الوسائط. أو وضِّح للمستخدم سبب احتياج تطبيقك إلى الإذن:

Kotlin

override fun onActivityResult(requestCode: Int, resultCode: Int,
                 data: Intent?) {
    ...
    when (requestCode) {
        EDIT_REQUEST_CODE ->
            if (resultCode == Activity.RESULT_OK) {
                /* Edit request granted; proceed. */
            } else {
                /* Edit request not granted; explain to the user. */
            }
    }
}

Java

@Override
protected void onActivityResult(int requestCode, int resultCode,
                   @Nullable Intent data) {
    ...
    if (requestCode == EDIT_REQUEST_CODE) {
        if (resultCode == Activity.RESULT_OK) {
            /* Edit request granted; proceed. */
        } else {
            /* Edit request not granted; explain to the user. */
        }
    }
}

يمكنك استخدام هذا النمط العام نفسه مع createFavoriteRequest()، createTrashRequest(), أو createDeleteRequest()

إذن إدارة الوسائط

قد يثق المستخدمون في تطبيق معيّن لإدارة الوسائط، مثل إنشاء إجراء تعديلات متكررة على ملفات الوسائط إذا كان تطبيقك يستهدف الإصدار 11 من نظام التشغيل Android أو الإصدارات الأحدث وليس تطبيق معرض الصور التلقائي على الجهاز يجب عرض مربّع حوار التأكيد للمستخدم في كل مرة يحاول فيها التطبيق تعديل ملف أو حذفه.

إذا كان تطبيقك يستهدف الإصدار 12 من نظام التشغيل Android (المستوى 31) أو إصدارًا أحدث، يمكنك طلب يمنح المستخدمون تطبيقك إذن الوصول الخاص إلى إدارة الوسائط. هذا النمط يسمح الإذن لتطبيقك بتنفيذ كلّ من الإجراءات التالية بدون الحاجة إلى طلب المستخدم لكل عملية في الملف:

للقيام بذلك، أكمل الخطوات التالية:

  1. قم بتعريف إذن MANAGE_MEDIA و READ_EXTERNAL_STORAGE إذن في ملف البيان لتطبيقك.

    للاتصال بـ createWriteRequest() بدون إظهار تأكيد أوضح مربع الحوار ACCESS_MEDIA_LOCATION إذن واحد أيضًا.

  2. عرض واجهة مستخدم في تطبيقك لتوضيح سبب رغبته في منح إدارة الوسائط في تطبيقك

  3. استدعاء ACTION_REQUEST_MANAGE_MEDIA الهدف من الإجراء. ينقل ذلك المستخدمين إلى شاشة تطبيقات إدارة الوسائط في إعدادات النظام. من هنا، يمكن للمستخدمين منح إذن الوصول الخاص للتطبيق.

حالات الاستخدام التي تتطلب بديلاً لمتجر الوسائط

إذا كان تطبيقك يؤدي أحد الأدوار التالية بشكل أساسي، ننصحك كبديل لواجهات برمجة تطبيقات MediaStore.

العمل مع أنواع أخرى من الملفات

إذا كان تطبيقك يعمل مع مستندات وملفات لا تحتوي على وسائط حصريًا المحتوى، مثل الملفات التي تستخدم امتداد الملف EPUB أو PDF، فاستخدم الملف إجراء ACTION_OPEN_DOCUMENT المطلوب، كما هو موضّح في دليل التخزين. والوصول إلى المستندات وغيرها .

مشاركة الملفات في التطبيقات المصاحبة

في الحالات التي تقدم فيها مجموعة من التطبيقات المصاحبة، مثل تطبيق مراسلة تطبيق للملف الشخصي، يمكنك إعداد مشاركة الملفات باستخدام معرّفات الموارد المنتظِمة content:// نقترح أيضًا سير العمل هذا باعتباره أفضل أدوات الأمان التدريب.

مصادر إضافية

لمزيد من المعلومات عن كيفية تخزين الوسائط والوصول إليها، يُرجى الرجوع إلى ما يلي: الموارد.

نماذج

الفيديوهات