تحدّد Jetpack Media3 واجهة Player
توضّح الوظائف الأساسية لتشغيل ملفات الفيديو والصوت. ExoPlayer
هو التنفيذ التلقائي لهذه الواجهة في Media3. ننصحك باستخدام ExoPlayer، لأنّه يوفّر مجموعة شاملة من الميزات التي تغطي معظم حالات استخدام التشغيل، ويمكن تخصيصه للتعامل مع أي حالات استخدام إضافية قد تحتاج إليها. يساعد ExoPlayer أيضًا في تجنُّب تجزئة الأجهزة وأنظمة التشغيل، ما يضمن عمل الرمز البرمجي بشكل متسق في جميع أنحاء نظام Android الأساسي. يتضمّن ExoPlayer ما يلي:
- إتاحة قوائم التشغيل
- إتاحة مجموعة متنوعة من تنسيقات البث التقدّمي والتكيّفي
- إتاحة إدراج الإعلانات من جهة العميل والخادم
- إتاحة تشغيل المحتوى المحمي بموجب إدارة الحقوق الرقمية
توضّح لك هذه الصفحة بعض الخطوات الأساسية لإنشاء تطبيق تشغيل، ويمكنك الانتقال إلى أدلّتنا الكاملة حول Media3 ExoPlayer لمعرفة المزيد من التفاصيل.
خطوات البدء:
للبدء، أضِف تبعية إلى وحدات ExoPlayer وUI وCommon في Jetpack Media3:
implementation "androidx.media3:media3-exoplayer:1.7.1" implementation "androidx.media3:media3-ui:1.7.1" implementation "androidx.media3:media3-common:1.7.1"
بناءً على حالة الاستخدام، قد تحتاج أيضًا إلى وحدات إضافية من Media3، مثل exoplayer-dash
لتشغيل المحتوى بتنسيق DASH.
تأكَّد من استبدال 1.7.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 Session:
implementation "androidx.media3:media3-session:1.7.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();
تتم مزامنة حالة Player
تلقائيًا مع حالة MediaSession
في Media3. تعمل هذه الطريقة مع أي عملية تنفيذ 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()
.
نشر إشعار
يجب أن تنشر الخدمات التي تعمل في المقدّمة إشعارًا أثناء نشاطها. سيؤدي ذلك إلى إنشاء MediaStyle
إشعار تلقائيًا في شكل MediaNotification
.MediaSessionService
لتوفير إشعار مخصّص، أنشئ
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
يحتوي على إشارات إضافية حول نوع شجرة المحتوى التي يهتم بها تطبيق العميل.