الاستماع إلى أحداث التشغيل
ويتم الإبلاغ عن الأحداث، مثل التغييرات في الحالة وأخطاء التشغيل، في حالات 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
- سيُقام
playWhenReady
true
- لا يتم منع التشغيل لسبب من الأسباب المعروضة من قِبل
"
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
لتشغيل المزيد من الأحداث، مثل البحث بعد نقل عنصر وسائط. - ويهدف المستمع إلى استخدام قيم حالة متعدّدة يتمّ الإبلاغ عنها
من خلال عمليات استدعاء منفصلة معًا، أو بالاقتران مع أساليب getter
Player
. على سبيل المثال، يكون استخدام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();