الاستماع إلى أحداث التشغيل
يتم إرسال الأحداث، مثل التغييرات في الحالة وأخطاء التشغيل، إلى مثيلات Player.Listener المسجّلة. لتسجيل أداة معالجة لتلقّي هذه الأحداث، اتّبِع الخطوات التالية:
Kotlin
// Add a listener to receive events from the player. player.addListener(listener)
Java
// Add a listener to receive events from the player. player.addListener(listener);
يحتوي Player.Listener على طرق تلقائية فارغة، لذا عليك تنفيذ الطرق التي تهمّك فقط. راجِع Javadoc للحصول على وصف كامل للطُرق ووقت استدعائها. في ما يلي شرح مفصّل لبعض أهم الطرق.
يمكن للمستمعين الاختيار بين تنفيذ عمليات ردّ الاتصال الخاصة بالأحداث الفردية أو عملية ردّ اتصال عامة onEvents يتم استدعاؤها بعد وقوع حدث واحد أو أكثر معًا. اطّلِع على Individual callbacks vs onEvents للحصول على توضيح بشأن الخيار الأفضل لحالات الاستخدام المختلفة.
التغييرات في حالة التشغيل
يمكن تلقّي التغييرات في حالة اللاعب من خلال تنفيذ
onPlaybackStateChanged(@State int state) في Player.Listener مسجَّل.
يمكن أن تكون حالة التشغيل واحدة من أربع حالات:
-
Player.STATE_IDLE: هذه هي الحالة الأولية، أي الحالة التي يكون فيها المشغّل متوقفًا، وعندما يتعذّر التشغيل. لن يتضمّن المشغّل سوى موارد محدودة في هذه الحالة. Player.STATE_BUFFERING: لا يمكن للمشغّل بدء التشغيل فورًا من الموضع الحالي. ويحدث ذلك غالبًا لأنّه يجب تحميل المزيد من البيانات.Player.STATE_READY: يمكن للمشغّل تشغيل المحتوى فورًا من الموضع الحالي.-
Player.STATE_ENDED: انتهى المشغّل من تشغيل جميع الوسائط.
بالإضافة إلى هذه الحالات، يتضمّن المشغّل علامة playWhenReady للإشارة إلى
نية المستخدم تشغيل المحتوى. يمكن تلقّي التغييرات في هذه العلامة من خلال تنفيذ onPlayWhenReadyChanged(playWhenReady, @PlayWhenReadyChangeReason int reason).
يكون المشغّل في وضع التشغيل (أي أنّ موضع التشغيل يتقدّم ويتم عرض الوسائط للمستخدم) عندما تستوفي جميع الشروط الثلاثة التالية:
- يكون المشغّل في حالة
Player.STATE_READY - سيُقام
playWhenReadytrue - لا يتم إيقاف التشغيل مؤقتًا بسبب تم إرجاعه بواسطة
Player.getPlaybackSuppressionReason
بدلاً من الاضطرار إلى التحقّق من هذه السمات بشكل فردي، يمكن استدعاء Player.isPlaying. يمكن تلقّي التغييرات في هذه الحالة من خلال تنفيذ onIsPlayingChanged(boolean isPlaying):
Kotlin
player.addListener( object : Player.Listener { override fun onIsPlayingChanged(isPlaying: Boolean) { if (isPlaying) { // Active playback. } else { // Not playing because playback is paused, ended, suppressed, or the player // is buffering, stopped or failed. Check player.playWhenReady, // player.playbackState, player.playbackSuppressionReason and // player.playerError for details. } } } )
Java
player.addListener( new Player.Listener() { @Override public void onIsPlayingChanged(boolean isPlaying) { if (isPlaying) { // Active playback. } else { // Not playing because playback is paused, ended, suppressed, or the player // is buffering, stopped or failed. Check player.getPlayWhenReady, // player.getPlaybackState, player.getPlaybackSuppressionReason and // player.getPlaybackError for details. } } });
أخطاء التشغيل
يمكن تلقّي الأخطاء التي تؤدي إلى تعذُّر التشغيل من خلال تنفيذ
onPlayerError(PlaybackException error) في Player.Listener مسجَّل. عند حدوث خطأ، سيتم استدعاء هذه الطريقة مباشرةً قبل أن تنتقل حالة التشغيل إلى Player.STATE_IDLE. يمكن إعادة محاولة تشغيل المحتوى الذي تعذّر تشغيله أو تم إيقافه من خلال استدعاء ExoPlayer.prepare.
يُرجى العِلم أنّ بعض عمليات تنفيذ Player تمرّر مثيلات من الفئات الفرعية من PlaybackException لتوفير معلومات إضافية حول الخطأ. على سبيل المثال، يمرّر ExoPlayer القيمة ExoPlaybackException التي تتضمّن type وrendererIndex وحقولاً أخرى خاصة بـ ExoPlayer.
يوضّح المثال التالي كيفية رصد تعذُّر التشغيل بسبب مشكلة في شبكة HTTP:
Kotlin
player.addListener( object : Player.Listener { override fun onPlayerError(error: PlaybackException) { val cause = error.cause if (cause is HttpDataSourceException) { // An HTTP error occurred. val httpError = cause // It's possible to find out more about the error both by casting and by querying // the cause. if (httpError is InvalidResponseCodeException) { // Cast to InvalidResponseCodeException and retrieve the response code, message // and headers. } else { // Try calling httpError.getCause() to retrieve the underlying cause, although // note that it may be null. } } } } )
Java
player.addListener( new Player.Listener() { @Override public void onPlayerError(PlaybackException error) { @Nullable Throwable cause = error.getCause(); if (cause instanceof HttpDataSourceException) { // An HTTP error occurred. HttpDataSourceException httpError = (HttpDataSourceException) cause; // It's possible to find out more about the error both by casting and by querying // the cause. if (httpError instanceof HttpDataSource.InvalidResponseCodeException) { // Cast to InvalidResponseCodeException and retrieve the response code, message // and headers. } else { // Try calling httpError.getCause() to retrieve the underlying cause, although // note that it may be null. } } } });
الانتقالات بين قوائم التشغيل
عندما ينتقل المشغّل إلى عنصر وسائط جديد في قائمة التشغيل، يتم استدعاء onMediaItemTransition(MediaItem mediaItem, @MediaItemTransitionReason int
reason) على عناصر Player.Listener المسجّلة. يشير السبب إلى ما إذا كان الانتقال تلقائيًا أو نتيجة البحث (على سبيل المثال، بعد طلب player.next()) أو تكرار العنصر نفسه أو بسبب تغيير قائمة التشغيل (على سبيل المثال، إذا تمت إزالة العنصر الذي يتم تشغيله حاليًا).
البيانات الوصفية
يمكن أن تتغيّر البيانات الوصفية التي يتم عرضها من player.getCurrentMediaMetadata() لأسباب عديدة، مثل عمليات الانتقال بين قوائم التشغيل أو تعديل البيانات الوصفية أثناء عرض الفيديو أو تعديل MediaItem الحالي أثناء التشغيل.
إذا كنت مهتمًا بتغييرات البيانات الوصفية، مثلاً لتعديل واجهة مستخدم تعرض العنوان الحالي، يمكنك الاستماع إلى onMediaMetadataChanged.
جارٍ تفعيل عناصر الانتقال
يؤدي استدعاء طرق Player.seekTo إلى سلسلة من عمليات الاستدعاء إلى مثيلات Player.Listener المسجّلة:
-
onPositionDiscontinuityمعreason=DISCONTINUITY_REASON_SEEKوهذه هي النتيجة المباشرة لاستدعاءPlayer.seekTo. يحتوي ردّ الاتصال على حقلَيPositionInfoللموضع قبل البحث وبعده. onPlaybackStateChangedمع أي تغيير فوري في الحالة مرتبط بالبحث. يُرجى العِلم أنّه قد لا يحدث مثل هذا التغيير.
عمليات إعادة الاستدعاء الفردية مقابل onEvents
يمكن للمستمعين الاختيار بين تنفيذ عمليات ردّ اتصال فردية مثل
onIsPlayingChanged(boolean isPlaying) وعملية ردّ الاتصال العامة onEvents(Player
player, Events events). يتيح ردّ الاتصال العام الوصول إلى العنصر Player ويحدّد مجموعة events التي حدثت معًا. يتم دائمًا استدعاء دالة الاستدعاء هذه بعد دوال الاستدعاء التي تتوافق مع الأحداث الفردية.
Kotlin
override fun onEvents(player: Player, events: Player.Events) { if ( events.contains(Player.EVENT_PLAYBACK_STATE_CHANGED) || events.contains(Player.EVENT_PLAY_WHEN_READY_CHANGED) ) { uiModule.updateUi(player) } }
Java
@Override public void onEvents(Player player, Events events) { if (events.contains(Player.EVENT_PLAYBACK_STATE_CHANGED) || events.contains(Player.EVENT_PLAY_WHEN_READY_CHANGED)) { uiModule.updateUi(player); } }
يجب تفضيل الأحداث الفردية في الحالات التالية:
- يهتم المستمع بمعرفة أسباب التغييرات. على سبيل المثال، الأسباب المقدَّمة للسمة
onPlayWhenReadyChangedأوonMediaItemTransition. - لا يستجيب المستمع إلا للقيم الجديدة المقدَّمة من خلال مَعلمات رد الاتصال أو يؤدي إلى إجراء آخر لا يعتمد على مَعلمات رد الاتصال.
- يفضّل تنفيذ المستمع إشارة واضحة قابلة للقراءة إلى ما أدّى إلى تشغيل الحدث في اسم الطريقة.
- يُرسِل المستمع تقارير إلى نظام إحصاءات يحتاج إلى معرفة جميع الأحداث الفردية وتغييرات الحالة.
يجب استخدام onEvents(Player player, Events events) العام في الحالات التالية:
- يريد المستمع تشغيل المنطق نفسه لأحداث متعدّدة. على سبيل المثال، تعديل واجهة المستخدم لكل من
onPlaybackStateChangedوonPlayWhenReadyChanged. - يحتاج المستمع إلى الوصول إلى العنصر
Playerلتفعيل أحداث أخرى، مثل البحث بعد انتقال عنصر وسائط. - ينوي المستمع استخدام قيم حالات متعدّدة يتم إرسال تقارير بها من خلال دوال رد اتصال منفصلة معًا، أو مع طرق
Playergetter. على سبيل المثال، لا يمكن استخدامPlayer.getCurrentWindowIndex()معTimelineالمقدَّم فيonTimelineChangedبأمان إلا من داخل دالةonEventsالردّ. - يهتمّ المستمع بمعرفة ما إذا كانت الأحداث قد وقعت معًا منطقيًا.
على سبيل المثال، من
onPlaybackStateChangedإلىSTATE_BUFFERINGبسبب انتقال عنصر وسائط.
في بعض الحالات، قد يحتاج المستمعون إلى دمج عمليات معاودة الاتصال الفردية مع عملية معاودة الاتصال العامة onEvents، مثلاً لتسجيل أسباب تغيير عنصر الوسائط باستخدام onMediaItemTransition، ولكن لا يمكن تنفيذ أي إجراء إلا بعد أن يصبح بالإمكان استخدام جميع تغييرات الحالة معًا في onEvents.
جارٍ استخدام AnalyticsListener
عند استخدام ExoPlayer، يمكن تسجيل AnalyticsListener لدى اللاعب
من خلال الاتصال بـ addAnalyticsListener. يمكن لعمليات تنفيذ AnalyticsListener الاستماع إلى الأحداث التفصيلية التي قد تكون مفيدة لأغراض التحليلات والتسجيل. يُرجى الرجوع إلى صفحة الإحصاءات لمزيد من التفاصيل.
جارٍ استخدام EventLogger
EventLogger هو AnalyticsListener تقدّمه المكتبة مباشرةً لأغراض التسجيل. أضِف EventLogger إلى ExoPlayer لتفعيل تسجيل إضافي مفيد
باستخدام سطر واحد:
Kotlin
player.addAnalyticsListener(EventLogger())
Java
player.addAnalyticsListener(new EventLogger());
يمكنك الاطّلاع على صفحة تسجيل بيانات التصحيح لمزيد من التفاصيل.
إطلاق الأحداث في مواضع تشغيل محدّدة
تتطلّب بعض حالات الاستخدام إطلاق الأحداث في مواضع تشغيل محدّدة. يمكن إجراء ذلك باستخدام PlayerMessage. يمكن إنشاء PlayerMessage باستخدام
ExoPlayer.createMessage. يمكن ضبط موضع التشغيل الذي يجب تنفيذها فيه باستخدام PlayerMessage.setPosition. يتم تنفيذ الرسائل في سلسلة عرض الفيديو تلقائيًا، ولكن يمكن تخصيص ذلك باستخدام PlayerMessage.setLooper. يمكن استخدام PlayerMessage.setDeleteAfterDelivery للتحكّم في ما إذا كان سيتم تنفيذ الرسالة في كل مرة يتم فيها الوصول إلى موضع التشغيل المحدّد (قد يحدث ذلك عدة مرات بسبب أوضاع البحث السريع والتكرار)، أو في المرة الأولى فقط. بعد إعداد PlayerMessage،
يمكن جدولة عمليات النسخ الاحتياطي باستخدام PlayerMessage.send.
Kotlin
player .createMessage { messageType: Int, payload: Any? -> } .setLooper(Looper.getMainLooper()) .setPosition(/* mediaItemIndex= */ 0, /* positionMs= */ 120000) .setPayload(customPayloadData) .setDeleteAfterDelivery(false) .send()
Java
player .createMessage( (messageType, payload) -> { // Do something at the specified playback position. }) .setLooper(Looper.getMainLooper()) .setPosition(/* mediaItemIndex= */ 0, /* positionMs= */ 120_000) .setPayload(customPayloadData) .setDeleteAfterDelivery(false) .send();