توفر جلسات الوسائط طريقة عالمية للتفاعل مع الصوت أو مشغّل الفيديو. في Media3، يكون المشغّل التلقائي هو فئة ExoPlayer
، وينفّذ
واجهة Player
. يتيح توصيل جلسة الوسائط بالمشغل للتطبيق الإعلان
عن تشغيل الوسائط خارجيًا وتلقي أوامر التشغيل من
مصادر خارجية.
قد تصدر الأوامر من أزرار فعلية مثل زر التشغيل في سماعة الرأس أو وحدة التحكّم عن بُعد في التلفزيون. قد يكون مصدرها أيضًا تطبيقات العميل التي تحتوي على وحدة تحكم في الوسائط، مثل توجيه "الإيقاف المؤقت" إلى "مساعد Google". تفوض جلسة الوسائط هذه الأوامر إلى مشغل تطبيق الوسائط.
وقت اختيار جلسة وسائط
عند تنفيذ سياسة MediaSession
، ستسمح للمستخدمين بالتحكّم في التشغيل:
- من خلال سمّاعات الرأس غالبًا ما تكون هناك أزرار أو تفاعلات لمس يمكن للمستخدم إجراؤها على سماعات الرأس لتشغيل الوسائط أو إيقافها مؤقتًا أو الانتقال إلى المسار التالي أو السابق.
- من خلال التحدث إلى مساعد Google. ومن الأنماط الشائعة قول "OK Google إيقاف مؤقت" لإيقاف أي وسائط يتم تشغيلها حاليًا على الجهاز بشكل مؤقت.
- من خلال ساعة Wear OS يتيح ذلك سهولة الوصول إلى عناصر التحكم الأكثر شيوعًا في التشغيل أثناء التشغيل على هواتفهم.
- من خلال عناصر التحكم في الوسائط تعرض لوحة العرض الدوّارة هذه عناصر تحكم لكل جلسة وسائط قيد التشغيل.
- على التلفزيون تفعيل إجراءات تشمل أزرار التشغيل الفعلية، والتحكّم في تشغيل النظام الأساسي، وإدارة الطاقة (على سبيل المثال، في حال إيقاف التلفزيون أو مكبّر الصوت العمودي أو جهاز استقبال الصوت والفيديو أو تبديل مصدر الإدخال، يجب أن يتوقف التشغيل في التطبيق).
- وأي عمليات خارجية أخرى تحتاج إلى التأثير في التشغيل.
وهذا أمر رائع للعديد من حالات الاستخدام. وعلى وجه الخصوص، عليك التفكير بشدّة
في استخدام السمة MediaSession
في الحالات التالية:
- إذا كنت تبث محتوى فيديوهات طويلة، مثل الأفلام أو التلفزيون المباشر
- تبث محتوى صوتي طويل، مثل ملفات البودكاست أو قوائم التشغيل الموسيقية.
- أنت تنشئ تطبيق تلفزيون.
ومع ذلك، قد لا تتوافق بعض حالات الاستخدام مع MediaSession
. قد تحتاج إلى استخدام السمة Player
فقط في الحالات التالية:
- أنت تعرض محتوى قصيرًا، حيث يكون تفاعل المستخدمين وتفاعلهم أمرًا بالغ الأهمية.
- لا يتوفّر فيديو نشط واحد، مثلاً أثناء تنقّل المستخدم في القائمة ويتم عرض عدة فيديوهات على الشاشة في الوقت نفسه.
- أنت تعرض فيديو تقديمي أو شرحًا لمرة واحدة تتوقّع أن يشاهده المستخدم بشكل نشط.
- المحتوى يراعي الخصوصية ولا تريد وصول عمليات خارجية إلى البيانات الوصفية للوسائط (مثلاً وضع التصفّح المتخفي في المتصفح)
إذا كانت حالة الاستخدام لا تتناسب مع أي من الحالات المذكورة أعلاه، ننصحك بمواصلة تشغيل التطبيق عندما لا يتفاعل المستخدم بشكل نشط مع المحتوى. إذا كانت الإجابة "نعم"، ننصحك باختيار
"MediaSession
". إذا كانت الإجابة "لا"، ننصحك باستخدام العلامة "Player
"
بدلاً من ذلك.
إنشاء جلسة وسائط
تظهر جلسة الوسائط بجانب المشغّل الذي يديره. يمكنك إنشاء جلسة وسائط باستخدام Context
وكائن Player
. يجب إنشاء جلسة وسائط
وإعدادها عند الحاجة، مثل طريقة مراحل نشاط onStart()
أو onResume()
في Activity
أو Fragment
، أو طريقة onCreate()
في Service
التي تملك جلسة الوسائط والمشغّل المرتبط بها.
لإنشاء جلسة وسائط، عليك إعداد Player
وتقديمه إلى MediaSession.Builder
على النحو التالي:
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 على تحديث جلسة الوسائط تلقائيًا باستخدام حالة المشغّل. وبناءً على ذلك، لن تحتاج إلى معالجة عملية الربط يدويًا من لاعب إلى آخر.
يُعد هذا فاصلاً عن النهج القديم الذي كان يجب أن تنشئ فيه علامة PlaybackStateCompat
وتحافظ عليها بشكل مستقل عن المشغّل نفسه، على سبيل المثال
للإشارة إلى أي أخطاء.
معرّف الجلسة الفريد
بشكل تلقائي، ينشئ MediaSession.Builder
جلسة تحتوي على سلسلة فارغة على أنّها
معرّف الجلسة. ويكفي هذا إذا كان التطبيق يهدف إلى إنشاء مثيل جلسة واحد فقط، وهي الحالة الأكثر شيوعًا.
إذا أراد أحد التطبيقات إدارة مثيلات جلسات متعددة في الوقت نفسه، على التطبيق التأكّد من أنّ رقم تعريف الجلسة لكل جلسة فريد من نوعه. يمكن ضبط رقم تعريف الجلسة
عند إنشاء الجلسة باستخدام MediaSession.Builder.setId(String id)
.
إذا رأيت الخطأ IllegalStateException
يعطّل تطبيقك مع ظهور رسالة الخطأ IllegalStateException: Session ID must be unique. ID=
، من المحتمل أن يكون قد تم إنشاء جلسة بشكل غير متوقع قبل إصدار مثيل تم إنشاؤه مسبقًا بنفس المعرّف. لتجنُّب تسريب الجلسات بسبب خطأ في البرمجة، يتم رصد مثل هذه الحالات وإشعارها من خلال طلب استثناء.
منح التحكم للعملاء الآخرين
جلسة تشغيل الوسائط هي المفتاح للتحكم في التشغيل. وهي تتيح لك توجيه الأوامر من مصادر خارجية إلى المشغِّل الذي يؤدي وظيفة تشغيل الوسائط. وقد تكون هذه المصادر عبارة عن أزرار فعلية مثل زر التشغيل على سماعة الرأس أو جهاز التحكّم عن بُعد في التلفزيون، أو الأوامر غير المباشرة مثل توجيه "الإيقاف المؤقت" إلى "مساعد Google". ننصحك أيضًا بمنح إذن الوصول إلى نظام Android لتسهيل استخدام الإشعارات وشاشة القفل، أو منح إذن الوصول إلى ساعة Wear OS بحيث يمكنك التحكّم في التشغيل من خلفية شاشة الساعة. يمكن للعملاء الخارجيين استخدام وحدة تحكّم في الوسائط لإصدار أوامر تشغيل لتطبيق الوسائط، ويتم تلقّيها من خلال جلسة تشغيل الوسائط، والتي تؤدي في النهاية إلى تفويض هذه الأوامر إلى مشغّل الوسائط.
عندما تكون وحدة التحكم على وشك الاتصال بجلسة تشغيل الوسائط، يتم استدعاء طريقة onConnect()
. يمكنك استخدام علامة ControllerInfo
المقدَّمة لتحديد ما إذا كنت تريد قبول الطلب أو رفضه. ويمكنك الاطِّلاع على مثال لقبول طلب ربط في قسم بيان الأوامر المتاحة.
بعد الاتصال، يمكن لوحدة التحكم إرسال أوامر التشغيل إلى الجلسة. بعد ذلك تُفوض
الجلسة هذه الأوامر إلى اللاعب. تعالج الجلسة أوامر التشغيل وقوائم التشغيل المحددة في واجهة Player
تلقائيًا.
وتتيح لك طرق معاودة الاتصال الأخرى معالجة طلبات أوامر التشغيل المخصّصة وتعديل قائمة التشغيل مثلاً).
وتشمل عمليات الاستدعاء هذه على نحو مماثل عنصر ControllerInfo
لتتمكّن من تعديل طريقة الردّ على كل طلب على أساس كل مسؤول تحكّم بالبيانات.
تعديل قائمة التشغيل
يمكن لجلسة الوسائط تعديل قائمة التشغيل مباشرةً في المشغّل كما هو موضّح في
دليل ExoPlayer لقوائم التشغيل.
وبإمكان مسؤولي التحكّم بالبيانات أيضًا تعديل قائمة التشغيل في حال
توفّر أحد الخيارين COMMAND_SET_MEDIA_ITEM
أو COMMAND_CHANGE_MEDIA_ITEMS
لوحدة التحكّم.
عند إضافة عناصر جديدة إلى قائمة التشغيل، يتطلب المشغّل عادةً توفّر مثيل MediaItem
مع
معرّف موارد منتظم (URI) محدّد
لإتاحة تشغيلها. تتم تلقائيًا إعادة توجيه العناصر المضافة حديثًا إلى
أساليب اللاعبين مثل player.addMediaItem
إذا كان لها معرّف موارد منتظم (URI) محدّد.
إذا أردت تخصيص مثيلات MediaItem
التي تمّت إضافتها إلى المشغّل، يمكنك
تجاوز
onAddMediaItems()
.
وهذه الخطوة مطلوبة عندما تريد دعم وحدات التحكم التي تطلب وسائط بدون معرّف موارد منتظم (URI) محدّد. بدلاً من ذلك، يتم عادةً إعداد حقل واحد أو أكثر من الحقول التالية لوصف الوسائط المطلوبة في MediaItem
:
MediaItem.id
: معرّف عام يعرّف الوسائطMediaItem.RequestMetadata.mediaUri
: عنوان URI للطلب قد يستخدم مخططًا مخصّصًا وليس بالضرورة أن يشغّله المشغّل مباشرةً.MediaItem.RequestMetadata.searchQuery
: طلب بحث نصي، على سبيل المثال من "مساعد Google".MediaItem.MediaMetadata
: بيانات وصفية منظَّمة، مثل "title" أو "artist"
لمزيد من خيارات التخصيص لقوائم التشغيل الجديدة تمامًا، يمكنك أيضًا إلغاء علامة onSetMediaItems()
التي تتيح لك تحديد عنصر البداية والموضع في قائمة التشغيل. على سبيل المثال، يمكنك توسيع عنصر واحد مطلوب في قائمة تشغيل كاملة وتوجيه المشغّل للبدء من فهرس العنصر المطلوب في الأصل. يمكن العثور على نموذج تنفيذ onSetMediaItems()
لهذه الميزة في التطبيق التجريبي للجلسة.
إدارة التنسيق المخصص والأوامر المخصصة
توضّح الأقسام التالية كيفية الإعلان عن تنسيق مخصّص لأزرار الأوامر المخصّصة لتطبيقات العملاء، وتفويض وحدات التحكّم في إرسال الأوامر المخصّصة.
تحديد التنسيق المخصص للجلسة
للإشارة إلى تطبيقات العميل إلى عناصر التحكّم في التشغيل التي تريد عرضها للمستخدم، يمكنك ضبط تنسيق مخصّص للجلسة عند إنشاء MediaSession
في طريقة onCreate()
من أجل خدمتك.
Kotlin
override fun onCreate() { super.onCreate() val likeButton = CommandButton.Builder() .setDisplayName("Like") .setIconResId(R.drawable.like_icon) .setSessionCommand(SessionCommand(SessionCommand.COMMAND_CODE_SESSION_SET_RATING)) .build() val favoriteButton = CommandButton.Builder() .setDisplayName("Save to favorites") .setIconResId(R.drawable.favorite_icon) .setSessionCommand(SessionCommand(SAVE_TO_FAVORITES, Bundle())) .build() session = MediaSession.Builder(this, player) .setCallback(CustomMediaSessionCallback()) .setCustomLayout(ImmutableList.of(likeButton, favoriteButton)) .build() }
Java
@Override public void onCreate() { super.onCreate(); CommandButton likeButton = new CommandButton.Builder() .setDisplayName("Like") .setIconResId(R.drawable.like_icon) .setSessionCommand(new SessionCommand(SessionCommand.COMMAND_CODE_SESSION_SET_RATING)) .build(); CommandButton favoriteButton = new CommandButton.Builder() .setDisplayName("Save to favorites") .setIconResId(R.drawable.favorite_icon) .setSessionCommand(new SessionCommand(SAVE_TO_FAVORITES, new Bundle())) .build(); Player player = new ExoPlayer.Builder(this).build(); mediaSession = new MediaSession.Builder(this, player) .setCallback(new CustomMediaSessionCallback()) .setCustomLayout(ImmutableList.of(likeButton, favoriteButton)) .build(); }
تعريف المشغّل المتاح والأوامر المخصصة
يمكن لتطبيقات الوسائط تحديد أوامر مخصصة يمكن استخدامها على سبيل المثال في
تخطيط مخصص. على سبيل المثال، قد ترغب في تنفيذ الأزرار التي تسمح للمستخدم
بحفظ عنصر وسائط في قائمة بالعناصر المفضلة. يرسل MediaController
أوامر مخصّصة ويتلقّىها MediaSession.Callback
.
يمكنك تحديد أوامر الجلسات المخصّصة المتاحة لجهاز
MediaController
عندما يتصل بجلسة الوسائط. يمكنك تحقيق ذلك من خلال
تجاوز MediaSession.Callback.onConnect()
. اضبط مجموعة الأوامر المتاحة وأرجعها عند قبول طلب اتصال من MediaController
في طريقة معاودة الاتصال onConnect
:
Kotlin
private inner class CustomMediaSessionCallback: MediaSession.Callback { // Configure commands available to the controller in onConnect() override fun onConnect( session: MediaSession, controller: MediaSession.ControllerInfo ): MediaSession.ConnectionResult { val sessionCommands = ConnectionResult.DEFAULT_SESSION_COMMANDS.buildUpon() .add(SessionCommand(SAVE_TO_FAVORITES, Bundle.EMPTY)) .build() return AcceptedResultBuilder(session) .setAvailableSessionCommands(sessionCommands) .build() } }
Java
class CustomMediaSessionCallback implements MediaSession.Callback { // Configure commands available to the controller in onConnect() @Override public ConnectionResult onConnect( MediaSession session, ControllerInfo controller) { SessionCommands sessionCommands = ConnectionResult.DEFAULT_SESSION_COMMANDS.buildUpon() .add(new SessionCommand(SAVE_TO_FAVORITES, new Bundle())) .build(); return new AcceptedResultBuilder(session) .setAvailableSessionCommands(sessionCommands) .build(); } }
لتلقّي طلبات أوامر مخصّصة من MediaController
، عليك إلغاء طريقة
onCustomCommand()
في Callback
.
Kotlin
private inner class CustomMediaSessionCallback: MediaSession.Callback { ... override fun onCustomCommand( session: MediaSession, controller: MediaSession.ControllerInfo, customCommand: SessionCommand, args: Bundle ): ListenableFuture<SessionResult> { if (customCommand.customAction == SAVE_TO_FAVORITES) { // Do custom logic here saveToFavorites(session.player.currentMediaItem) return Futures.immediateFuture( SessionResult(SessionResult.RESULT_SUCCESS) ) } ... } }
Java
class CustomMediaSessionCallback implements MediaSession.Callback { ... @Override public ListenableFuture<SessionResult> onCustomCommand( MediaSession session, ControllerInfo controller, SessionCommand customCommand, Bundle args ) { if(customCommand.customAction.equals(SAVE_TO_FAVORITES)) { // Do custom logic here saveToFavorites(session.getPlayer().getCurrentMediaItem()); return Futures.immediateFuture( new SessionResult(SessionResult.RESULT_SUCCESS) ); } ... } }
يمكنك تتبُّع وحدة التحكّم في الوسائط التي ترسِل طلبًا باستخدام السمة
packageName
في العنصر MediaSession.ControllerInfo
الذي تم
تمريره إلى طرق Callback
. ويتيح لك ذلك تخصيص سلوك تطبيقك استجابةً لأمر معيّن إذا كان ينشأ من النظام أو من تطبيقك أو تطبيقات العميل الأخرى.
تعديل التنسيق المخصص بعد تفاعل المستخدم
بعد التعامل مع أمر مخصص أو أي تفاعل آخر مع المشغّل، قد تحتاج إلى تحديث التنسيق المعروض في واجهة مستخدم وحدة التحكم. ومن الأمثلة النموذجية على ذلك زر التبديل الذي يغيّر رمزه بعد تشغيل الإجراء المرتبط بهذا الزر. لتعديل التنسيق، يمكنك استخدام
MediaSession.setCustomLayout
:
Kotlin
val removeFromFavoritesButton = CommandButton.Builder() .setDisplayName("Remove from favorites") .setIconResId(R.drawable.favorite_remove_icon) .setSessionCommand(SessionCommand(REMOVE_FROM_FAVORITES, Bundle())) .build() mediaSession.setCustomLayout(ImmutableList.of(likeButton, removeFromFavoritesButton))
Java
CommandButton removeFromFavoritesButton = new CommandButton.Builder() .setDisplayName("Remove from favorites") .setIconResId(R.drawable.favorite_remove_icon) .setSessionCommand(new SessionCommand(REMOVE_FROM_FAVORITES, new Bundle())) .build(); mediaSession.setCustomLayout(ImmutableList.of(likeButton, removeFromFavoritesButton));
تخصيص سلوك طلبات التشغيل
لتخصيص سلوك أمر تم تحديده في واجهة Player
، مثل
play()
أو seekToNext()
، يمكنك لف Player
في ForwardingPlayer
.
Kotlin
val player = ExoPlayer.Builder(context).build() val forwardingPlayer = object : ForwardingPlayer(player) { override fun play() { // Add custom logic super.play() } override fun setPlayWhenReady(playWhenReady: Boolean) { // Add custom logic super.setPlayWhenReady(playWhenReady) } } val mediaSession = MediaSession.Builder(context, forwardingPlayer).build()
Java
ExoPlayer player = new ExoPlayer.Builder(context).build(); ForwardingPlayer forwardingPlayer = new ForwardingPlayer(player) { @Override public void play() { // Add custom logic super.play(); } @Override public void setPlayWhenReady(boolean playWhenReady) { // Add custom logic super.setPlayWhenReady(playWhenReady); } }; MediaSession mediaSession = new MediaSession.Builder(context, forwardingPlayer).build();
للمزيد من المعلومات حول ForwardingPlayer
، يُرجى الاطّلاع على دليل ExoPlayer حول
التخصيص.
تحديد وحدة التحكّم التي تطلب عناصر التحكّم في المشغّل
عندما تنشأ مكالمة إلى إجراء Player
من خلال MediaController
، يمكنك تحديد مصدر المنشأ باستخدام MediaSession.controllerForCurrentRequest
والحصول على ControllerInfo
للطلب الحالي:
Kotlin
class CallerAwareForwardingPlayer(player: Player) : ForwardingPlayer(player) { override fun seekToNext() { Log.d( "caller", "seekToNext called from package ${session.controllerForCurrentRequest?.packageName}" ) super.seekToNext() } }
Java
public class CallerAwareForwardingPlayer extends ForwardingPlayer { public CallerAwareForwardingPlayer(Player player) { super(player); } @Override public void seekToNext() { Log.d( "caller", "seekToNext called from package: " + session.getControllerForCurrentRequest().getPackageName()); super.seekToNext(); } }
الاستجابة لأزرار الوسائط
أزرار الوسائط هي أزرار الأجهزة الموجودة على أجهزة Android والأجهزة الملحقة
الأخرى، مثل زر التشغيل/الإيقاف المؤقت على سماعة رأس بلوتوث. يتعامل Media3 مع
أحداث زر الوسائط نيابة عنك عند وصوله إلى الجلسة ويطلب طريقة Player
المناسبة على مشغّل الجلسة.
يمكن لأي تطبيق إلغاء السلوك التلقائي من خلال تجاوز
MediaSession.Callback.onMediaButtonEvent(Intent)
. في هذه الحالة، يمكن/يحتاج التطبيق إلى معالجة جميع تفاصيل واجهة برمجة التطبيقات من تلقاء نفسه.