يحدّد Jetpack Media3 واجهة Player
التي توضّح الوظائف الأساسية
لتشغيل ملفات الفيديو والصوت. ExoPlayer
هو التنفيذ التلقائي
لهذه الواجهة في Media3. ننصح باستخدام ExoPlayer لأنّها توفّر مجموعة شاملة من الميزات التي تشمل معظم حالات استخدام التشغيل ويمكن تخصيصها للتعامل مع أي حالات استخدام إضافية قد تكون لديك. تزيل ExoPlayer أيضًا
تقسيم الجهاز ونظام التشغيل حتى تعمل التعليمات البرمجية
بشكل متسق عبر منظومة Android المتكاملة. يتضمّن ExoPlayer ما يلي:
- دعم قوائم التشغيل
- إتاحة مجموعة متنوعة من تنسيقات البث التدريجي والتكيّفي
- دعم ميزة إدراج الإعلان من جهة العميل والخادم على حد سواء
- إتاحة التشغيل المحمي بموجب إدارة الحقوق الرقمية
ترشدك هذه الصفحة إلى بعض الخطوات الأساسية لإنشاء تطبيق تشغيل، وللحصول على مزيد من التفاصيل، يمكنك الانتقال إلى أدلةنا الكاملة حول 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
أو
أيّ عملية تنفيذ مخصّصة.
منح التحكم للعملاء الآخرين
يمكن لتطبيقات العملاء استخدام وحدة تحكّم في الوسائط
للتحكّم في تشغيل جلسة تشغيل الوسائط. ولتلقّي هذه الطلبات، يمكنك ضبط كائن callback عند
إنشاء MediaSession
.
عندما تكون وحدة التحكم على وشك الاتصال بجلسة تشغيل الوسائط، يتم استدعاء طريقة onConnect()
. يمكنك استخدام علامة ControllerInfo
المقدَّمة لتحديد ما إذا كنت تريد قبول الطلب أو رفضه. اطّلِع على مثال على ذلك في تطبيق Media3 Session التجريبي.
بعد الاتصال، يمكن لوحدة التحكم إرسال أوامر التشغيل إلى الجلسة. بعد ذلك تُفوض
الجلسة هذه الأوامر إلى اللاعب. تعالج الجلسة أوامر التشغيل وقوائم التشغيل المحددة في واجهة 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
مع إشارات إضافية حول نوع شجرة المحتوى الذي يهتم به تطبيق العميل.