لتوفير تجربة أكثر ثراءً للمستخدم، تتيح العديد من التطبيقات للمستخدمين المساهمة والوصول إلى الوسائط المتوفرة على وحدة تخزين خارجية. إطار العمل يوفّر فهرسًا محسَّنًا لمجموعات الوسائط، ما يُعرف باسم متجر الوسائط، يتيح للمستخدمين استرداد ملفات الوسائط هذه وتعديلها بسهولة أكبر. بالتساوي بعد إلغاء تثبيت تطبيقك، تبقى هذه الملفات على جهاز المستخدم.
أداة اختيار الصور
كبديل لاستخدام متجر الوسائط، يمكن استخدام أداة "اختيار الصور" من 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.Images
"،
MediaStore.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()
في كل مرة تعالج فيها صفًا من نتيجة طلب البحث. - أضِف المعرّف إلى معرّف الموارد المتسلسل للمحتوى كما هو موضّح في هذا المثال.
- تتطلّب الأجهزة التي تعمل بنظام التشغيل 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
API بدلاً من ذلك.
عمود البيانات
عند الوصول إلى ملف وسائط حالي، يمكنك استخدام قيمة عمود
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();
الموقع الجغرافي الذي تم فيه التقاط الوسائط
تحتوي بعض الصور ومقاطع الفيديو على معلومات الموقع في بياناتها الوصفية، يعرض مكان التقاط الصورة أو مكان تصوير فيديو تسجيل البيانات.
تعتمد طريقة الوصول إلى معلومات الموقع الجغرافي هذه في تطبيقك على ما إذا كان عليك الوصول إلى معلومات الموقع الجغرافي لصورة أو فيديو.
صور فوتوغرافية
إذا كان تطبيقك يستخدم مساحة تخزين ذات نطاق واسع، سيتم تفعيل يخفي النظام معلومات الموقع تلقائيًا. للوصول إلى هذه المعلومات، أكمل الخطوات التالية:
- اطلب
ACCESS_MEDIA_LOCATION
الإذن في بيان التطبيق. من الكائن
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) أو إصدارًا أحدث، يمكنك طلب يمنح المستخدمون تطبيقك إذن الوصول الخاص إلى إدارة الوسائط. هذا النمط يسمح الإذن لتطبيقك بتنفيذ كلّ من الإجراءات التالية بدون الحاجة إلى طلب المستخدم لكل عملية في الملف:
- تعديل الملفات، باستخدام
createWriteRequest()
- يمكنك نقل الملفات إلى المهملات وخارجها باستخدام
createTrashRequest()
- حذف الملفات، باستخدام
createDeleteRequest()
للقيام بذلك، أكمل الخطوات التالية:
قم بتعريف إذن
MANAGE_MEDIA
وREAD_EXTERNAL_STORAGE
إذن في ملف البيان لتطبيقك.للاتصال بـ
createWriteRequest()
بدون إظهار تأكيد أوضح مربع الحوارACCESS_MEDIA_LOCATION
إذن واحد أيضًا.عرض واجهة مستخدم في تطبيقك لتوضيح سبب رغبته في منح إدارة الوسائط في تطبيقك
استدعاء
ACTION_REQUEST_MANAGE_MEDIA
الهدف من الإجراء. ينقل ذلك المستخدمين إلى شاشة تطبيقات إدارة الوسائط في إعدادات النظام. من هنا، يمكن للمستخدمين منح إذن الوصول الخاص للتطبيق.
حالات الاستخدام التي تتطلب بديلاً لمتجر الوسائط
إذا كان تطبيقك يؤدي أحد الأدوار التالية بشكل أساسي، ننصحك باستخدام بديل لواجهات برمجة تطبيقات MediaStore
.
العمل مع أنواع أخرى من الملفات
إذا كان تطبيقك يعمل مع مستندات وملفات لا تحتوي على وسائط حصريًا
المحتوى، مثل الملفات التي تستخدم امتداد الملف EPUB أو PDF، فاستخدم الملف
إجراء ACTION_OPEN_DOCUMENT
المطلوب، كما هو موضّح في دليل التخزين.
والوصول إلى المستندات وغيرها
.
مشاركة الملفات في التطبيقات المصاحبة
في الحالات التي تقدم فيها مجموعة من التطبيقات المصاحبة، مثل تطبيق مراسلة
تطبيق للملف الشخصي، يمكنك إعداد مشاركة الملفات
باستخدام معرّفات الموارد المنتظِمة content://
نقترح أيضًا سير العمل هذا باعتباره أفضل أدوات الأمان
التدريب.
مصادر إضافية
لمزيد من المعلومات عن كيفية تخزين الوسائط والوصول إليها، يُرجى الرجوع إلى ما يلي: الموارد.
نماذج
- MediaStore، متوفرة على GitHub