تحدّد Jetpack Media3 واجهة Player
التي تحدّد الوظائف الأساسية
لتشغيل ملفات الفيديو والملفات الصوتية. ExoPlayer
هو التنفيذ التلقائي
لهذه الواجهة في Media3. ننصح باستخدام ExoPlayer لأنّه يوفّر مجموعة شاملة من الميزات التي تغطي معظم حالات استخدام التشغيل ويمكن تخصيصه للتعامل مع أي حالات استخدام إضافية قد تكون لديك. يعمل ExoPlayer أيضًا على تجريد تجزئة الأجهزة ونظام التشغيل لكي تعمل الرمز البرمجي بشكل متسق عبر منظومة Android المتكاملة. يشمل ExoPlayer ما يلي:
- دعم قوائم التشغيل
- التوافق مع مجموعة متنوعة من تنسيقات البث التقدمي والتكيّفي
- دعم ميزة إدراج الإعلانات من جهة العميل والخادم
- إتاحة التشغيل المحمي بموجب إدارة الحقوق الرقمية (DRM)
تقدّم لك هذه الصفحة إرشادات حول بعض الخطوات الأساسية لإنشاء تطبيق تشغيل، وللحصول على مزيد من التفاصيل، يمكنك الانتقال إلى الأدلّة الكاملة حول Media3 ExoPlayer.
البدء
للبدء، يجب إضافة تبعية على ExoPlayer وواجهة المستخدم والوحدات الشائعة من Jetpack Media3:
implementation "androidx.media3:media3-exoplayer:1.3.1" implementation "androidx.media3:media3-ui:1.3.1" implementation "androidx.media3:media3-common:1.3.1"
حسب حالة الاستخدام، قد تحتاج أيضًا إلى وحدات إضافية من Media3،
مثل exoplayer-dash
لتشغيل أحداث البث بتنسيق DASH.
تأكد من استبدال 1.3.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
مجموعة من خيارات التخصيص التي قد تهمّك، مثل:
setAudioAttributes()
لضبط إمكانية معالجة التركيز على الصوتsetHandleAudioBecomingNoisy()
لضبط سلوك التشغيل عند فصل جهاز إخراج الصوتsetTrackSelector()
لضبط اختيار المقطع الصوتي
يوفّر Media3 مكوّن PlayerView
في واجهة المستخدم يمكنك تضمينه في ملف تنسيق تطبيقك. يتضمن هذا المكوِّن PlayerControlView
لعناصر التحكم في التشغيل، وSubtitleView
لعرض الترجمة، وSurface
لعرض الفيديو.
جارٍ تحضير المشغّل
أضِف عناصر الوسائط إلى قائمة تشغيل لتشغيلها بطُرق مثل setMediaItem()
وaddMediaItem()
.
بعد ذلك، يمكنك استدعاء prepare()
لبدء
تحميل الوسائط والحصول على الموارد اللازمة.
يجب عدم تنفيذ هذه الخطوات قبل تشغيل التطبيق في المقدّمة. أمّا إذا كان اللاعب من خلال Activity
أو Fragment
، يعني ذلك إعداد المشغّل في مستوى واجهة برمجة التطبيقات onStart()
والمستويات 24 والمستويات الأعلى أو onResume()
في المستوى 23 من واجهة برمجة التطبيقات والمستويات الأدنى. إذا كان لاعبًا في Service
،
يمكنك إعداده في onCreate()
.
التحكم في المشغّل
بعد الانتهاء من إعداد المشغّل، يمكنك التحكم في تشغيله من خلال طرق الاتصال المتاحة في المشغّل، مثل:
play()
وpause()
لبدء التشغيل وإيقافه مؤقتًاseekTo()
للانتقال إلى موضع داخل العنصر الإعلامي الحاليseekToNextMediaItem()
وseekToPreviousMediaItem()
للتنقّل في قائمة التشغيل
وسيتم تعديل مكونات واجهة المستخدم، مثل PlayerView
أو PlayerControlView
، وفقًا لذلك عند ربطها بالمشغّل.
حرر المشغّل
قد يتطلّب التشغيل موارد متوفرة بكمية محدودة، مثل برامج فك ترميز الفيديوهات، لذا من المهم طلب الرمز release()
على المشغّل لإخلاء بعض الموارد في حال لم يعد المشغّل مطلوبًا.
إذا كان المشغّل يستخدم Activity
أو Fragment
، فحرره وفقًا لطريقة مراحل النشاط
onStop()
في المستوى 24 من واجهة برمجة التطبيقات والمستويات الأعلى أو بالطريقة onPause()
في المستوى 23 من واجهة برمجة التطبيقات والمستويات الأدنى. إذا حصل لاعب في Service
، يمكنك إصداره في onDestroy()
.
إدارة التشغيل باستخدام جلسة وسائط
توفّر جلسات الوسائط على Android طريقة معيارية للتفاعل مع مشغّل الوسائط عبر حدود العملية. يتيح لك ربط جلسة وسائط بالمشغل الإعلان عن تشغيل الوسائط خارجيًا وتلقي أوامر التشغيل من مصادر خارجية، على سبيل المثال التكامل مع عناصر التحكم في وسائط النظام على الجوّال والأجهزة ذات الشاشات الكبيرة.
لاستخدام جلسات الوسائط، أضف تبعية على وحدة جلسة Media3:
implementation "androidx.media3:media3-session:1.3.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
أو
عملية تنفيذ مخصّصة.
منح إمكانية التحكم لعملاء آخرين
يمكن لتطبيقات العميل استخدام وحدة تحكّم في الوسائط
للتحكّم في تشغيل جلسة الوسائط. لتلقّي هذه الطلبات، يجب ضبط كائن معاودة الاتصال عند إنشاء 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
مع إشارات إضافية حول نوع شجرة المحتوى التي يهتم بها تطبيق العميل.