إنشاء تطبيق مشغّل وسائط أساسي باستخدام Media3 ExoPlayer

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

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

الخطوات الأولى

للبدء، أضف تبعية على ExoPlayer وواجهة المستخدم والوحدات الشائعة Jetpack Media3:

implementation "androidx.media3:media3-exoplayer:1.4.1"
implementation "androidx.media3:media3-ui:1.4.1"
implementation "androidx.media3:media3-common:1.4.1"

حسب حالة الاستخدام، قد تحتاج أيضًا إلى وحدات إضافية من Media3 مثل exoplayer-dash لتشغيل مجموعات البث بتنسيق DASH.

تأكَّد من استبدال 1.4.1 بالإصدار الذي تفضّله من المكتبة. يمكنك الرجوع إلى ملاحظات الإصدار لرؤية أحدث إصدار.

إنشاء مشغّل وسائط

من خلال Media3، يمكنك إما استخدام التنفيذ المضمّن في Player الخاصة بك، ExoPlayer، أو يمكنك إنشاء عملية تنفيذ مخصّصة لك.

إنشاء ExoPlayer

في ما يلي أبسط طريقة لإنشاء مثيل ExoPlayer:

Kotlin

val player = ExoPlayer.Builder(context).build()

Java

ExoPlayer player = new ExoPlayer.Builder(context).build();

يمكنك إنشاء مشغّل الوسائط في طريقة دورة حياة onCreate() من Activity أو Fragment أو Service مكان الإقامة

تشمل Builder مجموعة من خيارات التخصيص التي قد تهمّك، مثل:

يوفر Media3 مكوّن واجهة المستخدم PlayerView الذي يمكنك تضمينه في ملف التخطيط. يغلف هذا المكوِّن PlayerControlView للتشغيل عناصر التحكّم، وSubtitleView لعرض الترجمة، وSurface لعرض الترجمة الفيديو القادم.

جارٍ تحضير المشغّل

إضافة عناصر الوسائط إلى قائمة تشغيل لـ التشغيل باستخدام طرق مثل setMediaItem() وaddMediaItem() بعد ذلك، يمكنك الاتصال بالرقم prepare() وبدء تحميل الوسائط والحصول على الموارد اللازمة.

يجب عدم تنفيذ هذه الخطوات قبل تشغيل التطبيق في المقدّمة. إذا كان اللاعب في Activity أو Fragment، ما يعني أنّ اللاعب في وضع onStart() في المستوى 24 من واجهة برمجة التطبيقات والمستويات الأعلى أو onResume() على مستوى واجهة برمجة التطبيقات 23 والأقل. بالنسبة إلى لاعب في Service، يمكنك إعدادها في onCreate().

التحكم في المشغّل

بعد تجهيز المشغّل، يمكنك التحكّم في تشغيله من خلال طُرق الاتصال. على المشغِّل، مثل:

سيتم تحديث مكونات واجهة المستخدم مثل PlayerView أو PlayerControlView وفقًا لذلك عندما تكون مرتبطة بلاعب

حرر المشغّل

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

إذا كان المشغّل في Activity أو Fragment، ارفع إصبعك عن المشغّل في onStop() في المستوى 24 من واجهة برمجة التطبيقات والمستويات الأعلى أو onPause() على مستوى واجهة برمجة التطبيقات 23 والأقل. إذا كنت لاعبًا في Service، يمكنك: وطرحه في onDestroy().

إدارة التشغيل باستخدام جلسة وسائط

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

لاستخدام جلسات الوسائط، أضف تبعية على وحدة جلسة Media3:

implementation "androidx.media3:media3-session:1.4.1"

إنشاء جلسة وسائط

يمكنك إنشاء MediaSession بعد إعداد المشغِّل على النحو التالي:

Kotlin

val player = ExoPlayer.Builder(context).build()
val mediaSession = MediaSession.Builder(context, player).build()

Java

ExoPlayer player = new ExoPlayer.Builder(context).build();
MediaSession mediaSession = new MediaSession.Builder(context, player).build();

يزامن Media3 تلقائيًا حالة Player مع حالة MediaSession تعمل هذه الميزة مع أي عملية تنفيذ Player، بما في ذلك ExoPlayer أو CastPlayer أو والتنفيذ المخصص.

منح إمكانية التحكم لعملاء آخرين

يمكن لتطبيقات العميل تنفيذ وحدة تحكّم في الوسائط. للتحكّم في تشغيل جلسة الوسائط. لتلقّي هذه الطلبات، يجب ضبط الكائن callback عند في إنشاء MediaSession.

عندما تكون وحدة التحكم على وشك الاتصال بجلسة الوسائط، onConnect() المريض. يمكنك استخدام ControllerInfo المتوفرة. لاتخاذ قرار بشأن قبول أو رفض الطلب. يمكنك الاطّلاع على مثال على ذلك في تطبيق العرض التوضيحي لجلسة Media3.

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

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

تشغيل الوسائط في الخلفية

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

تنفيذ MediaSessionService

أنشِئ صفًا يمتد إلى MediaSessionService وأنشِئ مثيلاً لصفك. MediaSession في طريقة دورة حياة onCreate().

Kotlin

class PlaybackService : MediaSessionService() {
    private var mediaSession: MediaSession? = null

    // Create your Player and MediaSession in the onCreate lifecycle event
    override fun onCreate() {
        super.onCreate()
        val player = ExoPlayer.Builder(this).build()
        mediaSession = MediaSession.Builder(this, player).build()
    }

    // Remember to release the player and media session in onDestroy
    override fun onDestroy() {
        mediaSession?.run {
            player.release()
            release()
            mediaSession = null
        }
        super.onDestroy()
    }
}

Java

public class PlaybackService extends MediaSessionService {
    private MediaSession mediaSession = null;

    @Override
    public void onCreate() {
        super.onCreate();
        ExoPlayer player = new ExoPlayer.Builder(this).build();
        mediaSession = new MediaSession.Builder(this, player).build();
    }

    @Override
    public void onDestroy() {
        mediaSession.getPlayer().release();
        mediaSession.release();
        mediaSession = null;
        super.onDestroy();
    }
}

في ملف البيان، صف Service بغرض MediaSessionService فلترة وطلب إذن "FOREGROUND_SERVICE" لتشغيل واجهة برمجة تطبيقات الخدمة:

<service
    android:name=".PlaybackService"
    android:foregroundServiceType="mediaPlayback"
    android:exported="true">
    <intent-filter>
        <action android:name="androidx.media3.session.MediaSessionService"/>
    </intent-filter>
</service>

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

وأخيرًا، في الصف الذي أنشأته، عليك إلغاء طريقة onGetSession() للتحكّم في وصول العميل إلى جلسة الوسائط. إرجاع MediaSession لقبول طلب الربط، أو إرجاع null لرفض الطلب.

Kotlin

// This example always accepts the connection request
override fun onGetSession(
    controllerInfo: MediaSession.ControllerInfo
): MediaSession? = mediaSession

Java

@Override
public MediaSession onGetSession(MediaSession.ControllerInfo controllerInfo) {
  // This example always accepts the connection request
  return mediaSession;
}

جارٍ الاتصال بواجهة المستخدم

بعد أن أصبحت جلسة الوسائط في Service منفصلة عن Activity أو Fragment حيث تظهر واجهة مستخدم اللاعب، يمكنك استخدام MediaController للربط معًا. في طريقة onStart() من Activity أو Fragment مع واجهة المستخدم، أنشئ SessionToken لـ MediaSession، ثم استخدِم SessionToken. لإنشاء MediaController. يتم إنشاء MediaController بشكل غير متزامن.

Kotlin

override fun onStart() {
  val sessionToken = SessionToken(this, ComponentName(this, PlaybackService::class.java))
  val controllerFuture = MediaController.Builder(this, sessionToken).buildAsync()
  controllerFuture.addListener(
    {
        // Call controllerFuture.get() to retrieve the MediaController.
        // MediaController implements the Player interface, so it can be
        // attached to the PlayerView UI component.
        playerView.setPlayer(controllerFuture.get())
      },
    MoreExecutors.directExecutor()
  )
}

Java

@Override
public void onStart() {
  SessionToken sessionToken =
    new SessionToken(this, new ComponentName(this, PlaybackService.class));
  ListenableFuture<MediaController> controllerFuture =
    new MediaController.Builder(this, sessionToken).buildAsync();
  controllerFuture.addListener(() -> {
    // Call controllerFuture.get() to retrieve the MediaController.
    // MediaController implements the Player interface, so it can be
    // attached to the PlayerView UI component.
    playerView.setPlayer(controllerFuture.get());
  }, MoreExecutors.directExecutor())
}

تنفِّذ MediaController الواجهة Player، لذا يمكنك استخدام الواجهة طرق مثل play() وpause() للتحكم في التشغيل. مشابه لعناصر أخرى المكونات، فتذكر إصدار MediaController إذا لم تعد المطلوبة، مثل طريقة دورة حياة onStop() في Activity، من خلال استدعاء MediaController.releaseFuture()

نشر إشعار

يجب السماح للخدمات التي تعمل في المقدّمة لنشر إشعار عندما تكون نشطة. حاسمة سيقوم MediaSessionService تلقائيًا بإنشاء إشعار واحد (MediaStyle) لـ في شكل MediaNotification. لتقديم إشعار مخصّص، يمكنك إنشاء MediaNotification.Provider مع DefaultMediaNotificationProvider.Builder أو عن طريق إنشاء تنفيذ مخصص لواجهة الموفر. إضافة مقدِّم الخدمة إلى MediaSession من خلال setMediaNotificationProvider

الترويج لمكتبة المحتوى الخاصة بك

تعتمد MediaLibraryService على MediaSessionService من خلال السماح للعميل التطبيقات لتصفّح محتوى الوسائط الذي يوفّره تطبيقك. تنفِّذ تطبيقات العميل MediaBrowser للتفاعل مع MediaLibraryService.

إنّ تنفيذ MediaLibraryService يشبه تنفيذ MediaSessionService، باستثناء أنّه في onGetSession()، يجب عرض MediaLibrarySession بدلاً من MediaSession مقارنةً بـ MediaSession.Callback، تشتمل السمة MediaLibrarySession.Callback على التي تسمح لعميل المتصفح بالتنقل عبر المحتوى الذي يقدمه خدمة المكتبة.

على غرار MediaSessionService، يجب الإفصاح عن السمة MediaLibraryService في البيان وطلب إذن "FOREGROUND_SERVICE" لتشغيل واجهة برمجة تطبيقات الخدمة:

<service
    android:name=".PlaybackService"
    android:foregroundServiceType="mediaPlayback"
    android:exported="true">
    <intent-filter>
        <action android:name="androidx.media3.session.MediaLibraryService"/>
        <action android:name="android.media.browse.MediaBrowserService"/>
    </intent-filter>
</service>

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

يتضمّن المثال أعلاه فلتر أهداف لكلٍّ من MediaLibraryService وللتوافق مع الأنظمة القديمة، يوفّر الإصدار MediaBrowserService القديم. تشير رسالة الأشكال البيانية يسمح فلتر الأهداف الإضافي لتطبيقات العميل باستخدام واجهة برمجة التطبيقات MediaBrowserCompat. للتعرّف على Service.

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

جارٍ إنشاء MediaLibrarySession

MediaLibrarySession توسيع واجهة برمجة تطبيقات MediaSession لإضافة واجهات برمجة تطبيقات لتصفّح المحتوى. بالمقارنة مع MediaSession معاودة الاتصال، معاودة الاتصال MediaLibrarySession تضيف طرق مثل:

  • onGetLibraryRoot() عندما يطلب العميل الجذر MediaItem من شجرة محتوى
  • onGetChildren() عندما يطلب العميل العناصر الثانوية لـ MediaItem في شجرة المحتوى
  • onGetSearchResult() عندما يطلب عميل نتائج بحث من شجرة المحتوى لمحتوى طلب

وستتضمّن طرق معاودة الاتصال ذات الصلة السمة LibraryParams. عنصر بإشارات إضافية حول نوع شجرة المحتوى الذي يستخدم تطبيق عميل المعنية.