أحداث اللاعبين

الاستماع إلى أحداث التشغيل

يتم الإبلاغ عن الأحداث، مثل التغييرات التي تطرأ على الحالة وأخطاء التشغيل، إلى السجلات المُسجّلة. 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 مثال:

  1. "onPositionDiscontinuity" مع "reason=DISCONTINUITY_REASON_SEEK" هذا هو النتيجة المباشرة لاستدعاء Player.seekTo. مدة معاودة الاتصال PositionInfo الحقول الخاصة بالموضع قبل التقديم وبعده.
  2. 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 لتشغيل أحداث أخرى، مثل التقديم أو الترجيع بعد الانتقال إلى عنصر وسائط
  • المستمع يريد استخدام قيم حالة متعددة يتم الإبلاغ عنها من خلال عمليات معاودة الاتصال منفصلة معًا، أو مع استدعاء واحد (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();