إنشاء مقدّم وسائط في السحابة الإلكترونية لنظام التشغيل Android

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

قبل البدء

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

الأهلية

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

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

تحديد ما إذا كنت بحاجة إلى إنشاء مقدّم خدمات وسائط سحابية

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

مقدّم خدمات سحابة إلكترونية نشط واحد لكل ملف شخصي

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

بشكل تلقائي، سيحاول تطبيق "أداة اختيار الصور" على Android اختيار مقدّم خدمات السحابة الإلكترونية تلقائيًا.

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

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

إنشاء مقدّم خدمة وسائط سحابية

يوضّح المخطّط التالي تسلسل الأحداث قبل وأثناء جلسة اختيار الصور بين تطبيق Android وأداة اختيار الصور في Android وMediaProvider على الجهاز المحلي وCloudMediaProvider.

مخطط تسلسلي يعرض مسار البيانات من أداة اختيار الصور إلى مقدّم خدمة وسائط سحابية
الشكل 1: مخطّط تسلسل الأحداث أثناء جلسة اختيار الصور
  1. يُهيئ النظام مقدّم خدمات السحابة الإلكترونية المفضّل لدى المستخدم، ويجري مزامنة دورية لبيانات الوسائط الوصفية مع الخلفية الخاصة بأداة اختيار الصور في Android.
  2. عندما يطلق تطبيق Android أداة اختيار الصور، وقبل عرض شبكة مدمجة من العناصر المحلية أو السحابية للمستخدم، تنفّذ أداة اختيار الصور عملية مزامنة تدريجية حساسة لوقت الاستجابة مع مقدّم خدمات السحابة الإلكترونية لضمان أن تكون النتائج حديثة قدر الإمكان. بعد تلقّي ردّ أو عند حلول الموعد النهائي، تعرض شبكة أداة اختيار الصور الآن جميع الصور التي يمكن الوصول إليها، وتجمع بين الصور المخزّنة محليًا على جهازك والصور التي تمت مزامنتها من السحابة الإلكترونية.
  3. أثناء تنقّل المستخدم، تسترد "أداة اختيار الصور" الصور المصغّرة للوسائط من مقدّم خدمة الوسائط على السحابة الإلكترونية لعرضها في واجهة المستخدم.
  4. عندما يكمل المستخدم الجلسة وتتضمّن النتائج عنصر وسائط على السحابة الإلكترونية، يطلب أداة اختيار الصور أوصاف الملفات للمحتوى، وينشئ معرّف موارد موحّد (URI)، ويمنح التطبيق الذي تم استدعاؤه إذن الوصول إلى الملف.
  5. يمكن للتطبيق الآن فتح معرّف الموارد الموحّد ولديه إذن وصول للقراءة فقط إلى محتوى الوسائط. يتم تلقائيًا تنقيح بيانات التعريف الحساسة. يستفيد أداة اختيار الصور من نظام ملفات FUSE لتنسيق عملية تبادل البيانات بين تطبيق Android ومزوّد الوسائط على السحابة الإلكترونية.

المشاكل الشائعة

في ما يلي بعض الاعتبارات المهمة التي يجب أخذها في الاعتبار عند التفكير في عملية التنفيذ:

تجنُّب الملفات المكرّرة

بما أنّه لا يمكن لأداة اختيار الصور في Android فحص حالة الوسائط على السحابة الإلكترونية، يجب أن يوفّر CloudMediaProvider MEDIA_STORE_URI في صف المؤشر لأي ملف متوفّر على السحابة الإلكترونية والجهاز المحلي، وإلا سيظهر للمستخدم ملفات مكرّرة في أداة اختيار الصور.

تحسين أحجام الصور لعرض المعاينة

من المهم جدًا أن يكون الملف الذي يتم عرضه من خلال onOpenPreview ليس الصورة الكاملة الدقة، وأن يلتزم بـ Size المطلوب. سيؤدي استخدام صورة كبيرة جدًا إلى إبطاء عملية التحميل في واجهة المستخدم، وقد تبدو الصورة صغيرة جدًا مشوّشة أو غير واضحة استنادًا إلى حجم شاشة الجهاز.

التعامل مع الاتجاه الصحيح

إذا كانت الصور المصغّرة التي يتم عرضها في onOpenPreview لا تتضمّن بيانات EXIF، يجب عرضها بالاتجاه الصحيح لتجنُّب تدويرها بشكل غير صحيح في شبكة المعاينة.

منع الوصول غير المصرَّح به

تحقَّق من MANAGE_CLOUD_MEDIA_PROVIDERS_PERMISSION قبل عرض البيانات على المتصل من ContentProvider. سيمنع ذلك التطبيقات غير المصرَّح بها من الوصول إلى البيانات على السحابة الإلكترونية.

فئة CloudMediaProvider

مشتقة من android.content.ContentProvider، تتضمّن الفئة CloudMediaProvider طرقًا مثل تلك الموضّحة في المثال التالي:

Kotlin

abstract class CloudMediaProvider : ContentProvider() {

    @NonNull
    abstract override fun onGetMediaCollectionInfo(@NonNull bundle: Bundle): Bundle

    @NonNull
    override fun onQueryAlbums(@NonNull bundle: Bundle): Cursor = TODO("Implement onQueryAlbums")

    @NonNull
    abstract override fun onQueryDeletedMedia(@NonNull bundle: Bundle): Cursor

    @NonNull
    abstract override fun onQueryMedia(@NonNull bundle: Bundle): Cursor

    @NonNull
    abstract override fun onOpenMedia(
        @NonNull string: String,
        @Nullable bundle: Bundle?,
        @Nullable cancellationSignal: CancellationSignal?
    ): ParcelFileDescriptor

    @NonNull
    abstract override fun onOpenPreview(
        @NonNull string: String,
        @NonNull point: Point,
        @Nullable bundle: Bundle?,
        @Nullable cancellationSignal: CancellationSignal?
    ): AssetFileDescriptor

    @Nullable
    override fun onCreateCloudMediaSurfaceController(
        @NonNull bundle: Bundle,
        @NonNull callback: CloudMediaSurfaceStateChangedCallback
    ): CloudMediaSurfaceController? = null
}

Java

public abstract class CloudMediaProvider extends android.content.ContentProvider {

  @NonNull
  public abstract android.os.Bundle onGetMediaCollectionInfo(@NonNull android.os.Bundle);

  @NonNull
  public android.database.Cursor onQueryAlbums(@NonNull android.os.Bundle);

  @NonNull
  public abstract android.database.Cursor onQueryDeletedMedia(@NonNull android.os.Bundle);

  @NonNull
  public abstract android.database.Cursor onQueryMedia(@NonNull android.os.Bundle);

  @NonNull
  public abstract android.os.ParcelFileDescriptor onOpenMedia(@NonNull String, @Nullable android.os.Bundle, @Nullable android.os.CancellationSignal) throws java.io.FileNotFoundException;

  @NonNull
  public abstract android.content.res.AssetFileDescriptor onOpenPreview(@NonNull String, @NonNull android.graphics.Point, @Nullable android.os.Bundle, @Nullable android.os.CancellationSignal) throws java.io.FileNotFoundException;

  @Nullable
  public android.provider.CloudMediaProvider.CloudMediaSurfaceController onCreateCloudMediaSurfaceController(@NonNull android.os.Bundle, @NonNull android.provider.CloudMediaProvider.CloudMediaSurfaceStateChangedCallback);
}

فئة CloudMediaProviderContract

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

Kotlin

object CloudMediaProviderContract {

    const val EXTRA_ALBUM_ID = "android.provider.extra.ALBUM_ID"
    const val EXTRA_LOOPING_PLAYBACK_ENABLED = "android.provider.extra.LOOPING_PLAYBACK_ENABLED"
    const val EXTRA_MEDIA_COLLECTION_ID = "android.provider.extra.MEDIA_COLLECTION_ID"
    const val EXTRA_PAGE_SIZE = "android.provider.extra.PAGE_SIZE"
    const val EXTRA_PAGE_TOKEN = "android.provider.extra.PAGE_TOKEN"
    const val EXTRA_PREVIEW_THUMBNAIL = "android.provider.extra.PREVIEW_THUMBNAIL"
    const val EXTRA_SURFACE_CONTROLLER_AUDIO_MUTE_ENABLED = "android.provider.extra.SURFACE_CONTROLLER_AUDIO_MUTE_ENABLED"
    const val EXTRA_SYNC_GENERATION = "android.provider.extra.SYNC_GENERATION"
    const val MANAGE_CLOUD_MEDIA_PROVIDERS_PERMISSION = "com.android.providers.media.permission.MANAGE_CLOUD_MEDIA_PROVIDERS"
    const val PROVIDER_INTERFACE = "android.content.action.CLOUD_MEDIA_PROVIDER"

    object MediaColumns {
        const val DATE_TAKEN_MILLIS = "date_taken_millis"
        const val DURATION_MILLIS = "duration_millis"
        const val HEIGHT = "height"
        const val ID = "id"
        const val IS_FAVORITE = "is_favorite"
        const val MEDIA_STORE_URI = "media_store_uri"
        const val MIME_TYPE = "mime_type"
        const val ORIENTATION = "orientation"
        const val SIZE_BYTES = "size_bytes"
        const val STANDARD_MIME_TYPE_EXTENSION = "standard_mime_type_extension"
        const val STANDARD_MIME_TYPE_EXTENSION_ANIMATED_WEBP = 3 // 0x3
        const val STANDARD_MIME_TYPE_EXTENSION_GIF = 1 // 0x1
        const val STANDARD_MIME_TYPE_EXTENSION_MOTION_PHOTO = 2 // 0x2
        const val STANDARD_MIME_TYPE_EXTENSION_NONE = 0 // 0x0
        const val SYNC_GENERATION = "sync_generation"
        const val WIDTH = "width"
    }

    object AlbumColumns {
        const val DATE_TAKEN_MILLIS = "date_taken_millis"
        const val DISPLAY_NAME = "display_name"
        const val ID = "id"
        const val MEDIA_COUNT = "album_media_count"
        const val MEDIA_COVER_ID = "album_media_cover_id"
    }

    object MediaCollectionInfo {
        const val ACCOUNT_CONFIGURATION_INTENT = "account_configuration_intent"
        const val ACCOUNT_NAME = "account_name"
        const val LAST_MEDIA_SYNC_GENERATION = "last_media_sync_generation"
        const val MEDIA_COLLECTION_ID = "media_collection_id"
    }
}

Java

public final class CloudMediaProviderContract {

  public static final String EXTRA_ALBUM_ID = "android.provider.extra.ALBUM_ID";
  public static final String EXTRA_LOOPING_PLAYBACK_ENABLED = "android.provider.extra.LOOPING_PLAYBACK_ENABLED";
  public static final String EXTRA_MEDIA_COLLECTION_ID = "android.provider.extra.MEDIA_COLLECTION_ID";
  public static final String EXTRA_PAGE_SIZE = "android.provider.extra.PAGE_SIZE";
  public static final String EXTRA_PAGE_TOKEN = "android.provider.extra.PAGE_TOKEN";
  public static final String EXTRA_PREVIEW_THUMBNAIL = "android.provider.extra.PREVIEW_THUMBNAIL";
  public static final String EXTRA_SURFACE_CONTROLLER_AUDIO_MUTE_ENABLED = "android.provider.extra.SURFACE_CONTROLLER_AUDIO_MUTE_ENABLED";
  public static final String EXTRA_SYNC_GENERATION = "android.provider.extra.SYNC_GENERATION";
  public static final String MANAGE_CLOUD_MEDIA_PROVIDERS_PERMISSION = "com.android.providers.media.permission.MANAGE_CLOUD_MEDIA_PROVIDERS";
  public static final String PROVIDER_INTERFACE = "android.content.action.CLOUD_MEDIA_PROVIDER";
}

// Columns available for every media item
public static final class CloudMediaProviderContract.MediaColumns {

  public static final String DATE_TAKEN_MILLIS = "date_taken_millis";
  public static final String DURATION_MILLIS = "duration_millis";
  public static final String HEIGHT = "height";
  public static final String ID = "id";
  public static final String IS_FAVORITE = "is_favorite";
  public static final String MEDIA_STORE_URI = "media_store_uri";
  public static final String MIME_TYPE = "mime_type";
  public static final String ORIENTATION = "orientation";
  public static final String SIZE_BYTES = "size_bytes";
  public static final String STANDARD_MIME_TYPE_EXTENSION = "standard_mime_type_extension";
  public static final int STANDARD_MIME_TYPE_EXTENSION_ANIMATED_WEBP = 3; // 0x3
  public static final int STANDARD_MIME_TYPE_EXTENSION_GIF = 1; // 0x1 
  public static final int STANDARD_MIME_TYPE_EXTENSION_MOTION_PHOTO = 2; // 0x2 
  public static final int STANDARD_MIME_TYPE_EXTENSION_NONE = 0; // 0x0 
  public static final String SYNC_GENERATION = "sync_generation";
  public static final String WIDTH = "width";
}

// Columns available for every album item
public static final class CloudMediaProviderContract.AlbumColumns {

  public static final String DATE_TAKEN_MILLIS = "date_taken_millis";
  public static final String DISPLAY_NAME = "display_name";
  public static final String ID = "id";
  public static final String MEDIA_COUNT = "album_media_count";
  public static final String MEDIA_COVER_ID = "album_media_cover_id";
}

// Media Collection metadata that is cached by the OS to compare sync states.
public static final class CloudMediaProviderContract.MediaCollectionInfo {

  public static final String ACCOUNT_CONFIGURATION_INTENT = "account_configuration_intent";
  public static final String ACCOUNT_NAME = "account_name";
  public static final String LAST_MEDIA_SYNC_GENERATION = "last_media_sync_generation";
  public static final String MEDIA_COLLECTION_ID = "media_collection_id";
}

onGetMediaCollectionInfo

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

Kotlin

abstract fun onGetMediaCollectionInfo(extras: Bundle): Bundle

Java

@NonNull
public abstract Bundle onGetMediaCollectionInfo(@NonNull Bundle extras);

تتضمّن حزمة MediaCollectionInfo التي يتم عرضها الثوابت التالية:

onQueryMedia

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

تعرض هذه الطريقة Cursor يمثّل جميع عناصر الوسائط في مجموعة الوسائط، ويمكن فلترتها اختياريًا حسب البيانات الإضافية المقدَّمة وترتيبها عكسيًا حسب تاريخ MediaColumns#DATE_TAKEN_MILLIS (تظهر أحدث العناصر أولاً).

تتضمّن حزمة CloudMediaProviderContract التي يتم عرضها الثوابت التالية:

على مقدّم خدمة الوسائط السحابية ضبط قيمة CloudMediaProviderContract#EXTRA_MEDIA_COLLECTION_ID كجزء من Bundle الذي تم إرجاعه. عدم ضبط هذه القيمة هو خطأ ويؤدي إلى إبطال Cursor المعروض. إذا كان مزوّد خدمة الوسائط على السحابة الإلكترونية قد عالج أي فلاتر في المحتوى الإضافي المقدَّم، عليه إضافة المفتاح إلى ContentResolver#EXTRA_HONORED_ARGS كجزء من Cursor#setExtras الذي تم عرضه.

onQueryDeletedMedia

يتم استخدام طريقة onQueryDeletedMedia() لضمان إزالة العناصر المحذوفة في حساب السحابة الإلكترونية بشكل صحيح من واجهة مستخدم أداة اختيار الصور. ونظرًا إلى أنّ هذه المكالمات قد تكون حساسة لوقت الاستجابة، قد يتم إجراؤها كجزء مما يلي:

  • المزامنة الاستباقية في الخلفية
  • جلسات "أداة اختيار الصور" (عند الحاجة إلى حالة مزامنة كاملة أو تدريجية)

تعطي واجهة مستخدم أداة اختيار الصور الأولوية لتجربة المستخدم السلسة، ولن تنتظر إلى أجل غير مسمى للحصول على رد. للحفاظ على سلاسة التفاعلات، قد تحدث مهلات. ستظل أي قيمة Cursor تم إرجاعها تحاول معالجتها في قاعدة بيانات أداة اختيار الصور للجلسات المستقبلية.

تعرض هذه الطريقة Cursor يمثّل جميع عناصر الوسائط المحذوفة في مجموعة الوسائط بأكملها ضمن إصدار مقدّم الخدمة الحالي كما هو معروض من خلال onGetMediaCollectionInfo(). يمكن فلترة هذه العناصر اختياريًا حسب الإضافات. على موفّر الوسائط السحابية ضبط CloudMediaProviderContract#EXTRA_MEDIA_COLLECTION_ID كجزء من Cursor#setExtras المعروض. ويُعد عدم ضبط هذا الإعداد خطأ ويؤدي إلى إبطال Cursor. إذا كان المزوّد قد تعامل مع أي فلاتر في الإضافات المقدَّمة، عليه إضافة المفتاح إلى ContentResolver#EXTRA_HONORED_ARGS.

onQueryAlbums

يتم استخدام طريقة onQueryAlbums() لاسترداد قائمة بألبومات Cloud المتوفّرة لدى مقدّم خدمات السحابة الإلكترونية والبيانات الوصفية المرتبطة بها. يمكنك الاطّلاع على CloudMediaProviderContract.AlbumColumns لمعرفة المزيد من التفاصيل.

تعرض هذه الطريقة Cursor يمثّل جميع عناصر الألبوم في مجموعة الوسائط، ويمكن فلترتها اختياريًا حسب الإضافات المتوفّرة وترتيبها بتسلسل زمني عكسي حسب AlbumColumns#DATE_TAKEN_MILLIS، مع عرض أحدث العناصر أولاً. على مقدّم خدمة الوسائط على السحابة الإلكترونية ضبط قيمة CloudMediaProviderContract#EXTRA_MEDIA_COLLECTION_ID كجزء من Cursor الذي يتم عرضه. عدم ضبط هذه القيمة هو خطأ ويؤدي إلى إبطال Cursor المعروض. إذا كان المزوّد قد عالج أي فلاتر في البيانات الإضافية المقدَّمة، عليه إضافة المفتاح إلى ContentResolver#EXTRA_HONORED_ARGS كجزء من Cursor الذي تم عرضه.

onOpenMedia

يجب أن يعرض الإجراء onOpenMedia() وسائط بالحجم الكامل تم تحديدها من خلال mediaId المقدَّم. إذا كانت هذه الطريقة تحظر تنزيل المحتوى على الجهاز، عليك التحقّق بشكل دوري من CancellationSignal المقدَّم لإلغاء الطلبات غير المكتملة.

onOpenPreview

يجب أن يعرض الإجراء onOpenPreview() صورة مصغّرة size للعنصر الذي يحمل رقم التعريف mediaId المقدَّم. يجب أن تكون الصورة المصغّرة بالتنسيق الأصلي CloudMediaProviderContract.MediaColumns#MIME_TYPE، ومن المتوقّع أن تكون درجة دقتها أقل بكثير من درجة دقة العنصر الذي تعرضه الدالة onOpenMedia. إذا تم حظر هذه الطريقة أثناء تنزيل المحتوى على الجهاز، عليك التحقّق بشكل دوري من CancellationSignal المقدَّم لإلغاء الطلبات غير المكتملة.

onCreateCloudMediaSurfaceController

يجب أن تعرض الطريقة onCreateCloudMediaSurfaceController() CloudMediaSurfaceController مستخدَمًا لعرض معاينة لعناصر الوسائط، أو null إذا كان عرض المعاينة غير متاح.

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

يتوافق CloudMediaSurfaceController مع قائمة عمليات معاودة الاتصال بدورة الحياة التالية: