توفّر جلسات الوسائط طريقة عالمية للتفاعل مع صوت أو فيديو
لاعب. في Media3، المُشغِّل التلقائي هو فئة ExoPlayer
التي تعمل على تنفيذ
الواجهة Player
. يؤدي ربط جلسة الوسائط بالمشغّل إلى السماح للتطبيق
للإعلان عن تشغيل الوسائط خارجيًا وتلقي أوامر التشغيل من
والمصادر الخارجية.
وقد تصدر الأوامر من الأزرار المادية، مثل زر التشغيل على لسماعة الرأس أو جهاز التحكم عن بعد للتلفزيون. وقد تأتي أيضًا من تطبيقات العميل التي تحتوي على وحدة التحكم في الوسائط، مثل إصدار أمر "إيقاف مؤقت" إلى "مساعد Google". وسائل الإعلام تفوّض الجلسة هذه الأوامر إلى مشغّل تطبيق الوسائط.
الوقت المناسب لاختيار جلسة وسائط
عند تنفيذ MediaSession
، تسمح للمستخدمين بالتحكّم في التشغيل:
- من خلال سماعات الرأس. غالبًا ما تكون هناك أزرار أو تفاعلات لمس يمكن للمستخدم تشغيل الوسائط على سماعات الرأس لتشغيل الوسائط أو إيقافها مؤقتًا أو الانتقال إلى الفيديو التالي أو المقطع الصوتي السابق.
- من خلال التحدّث إلى مساعد Google هناك نمط شائع وهو قول "حسنًا 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 لكي تتمكّن من التحكّم في تشغيل المحتوى من خلفية شاشة الساعة يمكن للعملاء الخارجيين استخدام وحدة التحكم في الوسائط لإصدار أوامر التشغيل إلى تطبيق الوسائط. وهي التي تتلقاها جلسة الوسائط، والتي في النهاية تفوض الأوامر إلى مشغّل الوسائط.
![رسم تخطيطي يوضح التفاعل بين MediaSession وMediaController.](https://developer.android.com/static/guide/topics/media/images/backgroundcontrols.png?authuser=6&hl=ar)
عندما تكون وحدة التحكم على وشك الاتصال بجلسة الوسائط،
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
: البيانات الوصفية المنظَّمة مثل "العنوان" أو "الفنان".
لمزيد من خيارات التخصيص لقوائم تشغيل جديدة بالكامل، يمكنك:
علاوةً على ذلك، إلغاء
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
على session Player.
يمكن للتطبيق إلغاء السلوك الافتراضي من خلال تجاوز
MediaSession.Callback.onMediaButtonEvent(Intent)
في هذه الحالة، لا يمكن
يمكن/يحتاج إلى معالجة جميع تفاصيل واجهة برمجة التطبيقات بمفرده.
خطأ أثناء المعالجة والإبلاغ
هناك نوعان من الأخطاء التي تُصدرها الجلسة وتُبلغ وحدات التحكّم. تشير الأخطاء الفادحة إلى تعذُّر تشغيل الجلسة بشكل فني. المستخدم الذي يقاطع تشغيل الموسيقى. إبلاغ وحدة التحكم عن الأخطاء الفادحة تلقائيًا عند حدوثها. الأخطاء غير الفادحة هي أخطاء غير تقنية أو متعلقة بالسياسات. لا تقاطع التشغيل ويتم إرسالها إلى وحدات التحكم من خلال تطبيقك يدويًا.
الأخطاء الفادحة في التشغيل
يتم إبلاغ الجلسة عن خطأ فادح في التشغيل، ثم
إبلاغ وحدات التحكم بالبيانات لطلبها
Player.Listener.onPlayerError(PlaybackException)
و
Player.Listener.onPlayerErrorChanged(@Nullable PlaybackException)
وفي هذه الحالة، يتم نقل حالة التشغيل إلى STATE_IDLE
تعرض MediaController.getPlaybackError()
قيمة PlaybackException
التي تسببت في
عملية الانتقال. يمكن لوحدة التحكّم فحص PlayerException.errorCode
للحصول على
معلومات عن سبب الخطأ.
بالنسبة إلى إمكانية التشغيل التفاعلي، يتم نسخ خطأ فادح إلى PlaybackStateCompat
لجلسة النظام الأساسي من خلال نقل حالتها إلى STATE_ERROR
وضبط
ورمز الخطأ والرسالة وفقًا لـ PlaybackException
.
تخصيص خطأ فادح
لتوفير معلومات مترجمة ومفيدة للمستخدم، رمز الخطأ
يمكن تخصيص كل من رسالة الخطأ والعناصر الإضافية للخطأ الفادح في التشغيل من خلال
استخدام ForwardingPlayer
عند إنشاء الجلسة:
Kotlin
val forwardingPlayer = ErrorForwardingPlayer(player) val session = MediaSession.Builder(context, forwardingPlayer).build()
Java
Player forwardingPlayer = new ErrorForwardingPlayer(player); MediaSession session = new MediaSession.Builder(context, forwardingPlayer).build();
يسجِّل مشغّل إعادة التوجيه Player.Listener
إلى المشغّل الفعلي.
ويعترض عمليات معاودة الاتصال التي تبلغ عن خطأ.
يتم بعد ذلك تفويض PlaybackException
للمستمعين الذين
يتم تسجيلها في مشغّل إعادة التوجيه لكي ينجح هذا الإجراء، يستخدم مشغّل إعادة التوجيه
يلغي Player.addListener
وPlayer.removeListener
للوصول إلى
أدوات معالجة البيانات التي يمكن من خلالها إرسال رمز خطأ أو رسالة أو ميزات إضافية مخصّصة:
Kotlin
class ErrorForwardingPlayer(private val context: Context, player: Player) : ForwardingPlayer(player) { private val listeners: MutableList<Player.Listener> = mutableListOf() private var customizedPlaybackException: PlaybackException? = null init { player.addListener(ErrorCustomizationListener()) } override fun addListener(listener: Player.Listener) { listeners.add(listener) } override fun removeListener(listener: Player.Listener) { listeners.remove(listener) } override fun getPlayerError(): PlaybackException? { return customizedPlaybackException } private inner class ErrorCustomizationListener : Player.Listener { override fun onPlayerErrorChanged(error: PlaybackException?) { customizedPlaybackException = error?.let { customizePlaybackException(it) } listeners.forEach { it.onPlayerErrorChanged(customizedPlaybackException) } } override fun onPlayerError(error: PlaybackException) { listeners.forEach { it.onPlayerError(customizedPlaybackException!!) } } private fun customizePlaybackException( error: PlaybackException, ): PlaybackException { val buttonLabel: String val errorMessage: String when (error.errorCode) { PlaybackException.ERROR_CODE_BEHIND_LIVE_WINDOW -> { buttonLabel = context.getString(R.string.err_button_label_restart_stream) errorMessage = context.getString(R.string.err_msg_behind_live_window) } // Apps can customize further error messages by adding more branches. else -> { buttonLabel = context.getString(R.string.err_button_label_ok) errorMessage = context.getString(R.string.err_message_default) } } val extras = Bundle() extras.putString("button_label", buttonLabel) return PlaybackException(errorMessage, error.cause, error.errorCode, extras) } override fun onEvents(player: Player, events: Player.Events) { listeners.forEach { it.onEvents(player, events) } } // Delegate all other callbacks to all listeners without changing arguments like onEvents. } }
Java
private static class ErrorForwardingPlayer extends ForwardingPlayer { private final Context context; private List<Player.Listener> listeners; @Nullable private PlaybackException customizedPlaybackException; public ErrorForwardingPlayer(Context context, Player player) { super(player); this.context = context; listeners = new ArrayList<>(); player.addListener(new ErrorCustomizationListener()); } @Override public void addListener(Player.Listener listener) { listeners.add(listener); } @Override public void removeListener(Player.Listener listener) { listeners.remove(listener); } @Nullable @Override public PlaybackException getPlayerError() { return customizedPlaybackException; } private class ErrorCustomizationListener implements Listener { @Override public void onPlayerErrorChanged(@Nullable PlaybackException error) { customizedPlaybackException = error != null ? customizePlaybackException(error, context) : null; for (int i = 0; i < listeners.size(); i++) { listeners.get(i).onPlayerErrorChanged(customizedPlaybackException); } } @Override public void onPlayerError(PlaybackException error) { for (int i = 0; i < listeners.size(); i++) { listeners.get(i).onPlayerError(checkNotNull(customizedPlaybackException)); } } private PlaybackException customizePlaybackException( PlaybackException error, Context context) { String buttonLabel; String errorMessage; switch (error.errorCode) { case PlaybackException.ERROR_CODE_BEHIND_LIVE_WINDOW: buttonLabel = context.getString(R.string.err_button_label_restart_stream); errorMessage = context.getString(R.string.err_msg_behind_live_window); break; // Apps can customize further error messages by adding more case statements. default: buttonLabel = context.getString(R.string.err_button_label_ok); errorMessage = context.getString(R.string.err_message_default); break; } Bundle extras = new Bundle(); extras.putString("button_label", buttonLabel); return new PlaybackException(errorMessage, error.getCause(), error.errorCode, extras); } @Override public void onEvents(Player player, Events events) { for (int i = 0; i < listeners.size(); i++) { listeners.get(i).onEvents(player, events); } } // Delegate all other callbacks to all listeners without changing arguments like onEvents. } }
الأخطاء غير الفادحة
يمكن إرسال الأخطاء غير الفادحة التي لا تنشأ عن استثناء فني. تطبيق إلى الكل أو إلى وحدة تحكُّم معيَّنة:
Kotlin
val sessionError = SessionError( SessionError.ERROR_SESSION_AUTHENTICATION_EXPIRED, context.getString(R.string.error_message_authentication_expired), ) // Sending a nonfatal error to all controllers. mediaSession.sendError(sessionError) // Interoperability: Sending a nonfatal error to the media notification controller to set the // error code and error message in the playback state of the platform media session. mediaSession.mediaNotificationControllerInfo?.let { mediaSession.sendError(it, sessionError) }
Java
SessionError sessionError = new SessionError( SessionError.ERROR_SESSION_AUTHENTICATION_EXPIRED, context.getString(R.string.error_message_authentication_expired)); // Sending a nonfatal error to all controllers. mediaSession.sendError(sessionError); // Interoperability: Sending a nonfatal error to the media notification controller to set the // error code and error message in the playback state of the platform media session. ControllerInfo mediaNotificationControllerInfo = mediaSession.getMediaNotificationControllerInfo(); if (mediaNotificationControllerInfo != null) { mediaSession.sendError(mediaNotificationControllerInfo, sessionError); }
يتم تكرار الخطأ غير الفادح الذي تم إرساله إلى وحدة التحكم في إشعارات الوسائط في
PlaybackStateCompat
من جلسة المنصة وبالتالي، لا تظهر إلا رمز الخطأ
يتم ضبط رسالة الخطأ على PlaybackStateCompat
وفقًا لذلك، بينما
لم يتم تغيير الحقل PlaybackStateCompat.state
إلى STATE_ERROR
.
تلقّي أخطاء غير فادحة
تتلقى MediaController
خطأً غير فادح من خلال التنفيذ
MediaController.Listener.onError
:
Kotlin
val future = MediaController.Builder(context, sessionToken) .setListener(object : MediaController.Listener { override fun onError(controller: MediaController, sessionError: SessionError) { // Handle nonfatal error. } }) .buildAsync()
Java
MediaController.Builder future = new MediaController.Builder(context, sessionToken) .setListener( new MediaController.Listener() { @Override public void onError(MediaController controller, SessionError sessionError) { // Handle nonfatal error. } });