האזנה לאירועי הפעלה
אירועים כמו שינויים במצב ושגיאות בהפעלה מדווחים למופעים רשומים של Player.Listener. כדי לרשום 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 מופיע תיאור מלא של השיטות ומתי הן נקראות. בהמשך מפורטות כמה מהשיטות החשובות ביותר.
למאזינים יש אפשרות להטמיע קריאות חוזרות (callback) לאירועים ספציפיים או קריאה חוזרת (callback) כללית של 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 רשום. אם מתרחשת שגיאה, ה-method הזו מופעלת מיד לפני שמצב ההפעלה משתנה ל-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. } } } });
מעברים בין פלייליסטים
בכל פעם שהנגן עובר לפריט מדיה חדש בפלייליסט, מתבצעת קריאה לאובייקטים הרשומים Player.Listener onMediaItemTransition(MediaItem mediaItem, @MediaItemTransitionReason int
reason). הסיבה מציינת אם זה היה מעבר אוטומטי, מעבר למיקום מסוים (לדוגמה, אחרי קריאה ל-player.next()), חזרה על אותו פריט או שינוי בפלייליסט (לדוגמה, אם הפריט שמופעל כרגע הוסר).
Metadata
יכול להיות שהמטא-נתונים שמוחזרים מ-player.getCurrentMediaMetadata() ישתנו בגלל הרבה סיבות: מעברים בין פלייליסטים, עדכונים של מטא-נתונים בתוך הסטרימינג או עדכון של MediaItem הנוכחי באמצע ההפעלה.
אם אתם רוצים לעקוב אחרי שינויים במטא-נתונים, למשל כדי לעדכן ממשק משתמש שמציג את השם הנוכחי, אתם יכולים להאזין ל-onMediaMetadataChanged.
מחפש
הפעלת שיטות Player.seekTo גורמת לסדרה של קריאות חוזרות (callbacks) למופעים רשומים של 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. - המאזין פועל רק על הערכים החדשים שמועברים דרך פרמטרים של קריאה חוזרת, או מפעיל משהו אחר שלא תלוי בפרמטרים של הקריאה החוזרת.
- ההטמעה של ה-listener מעדיפה אינדיקציה ברורה וקריאה לגבי מה שהפעיל את האירוע בשם השיטה.
- המאזין מדווח למערכת ניתוח נתונים שצריכה לדעת על כל אירוע בנפרד ועל כל שינוי במצב.
עדיף להשתמש ב-onEvents(Player player, Events events) הגנרי במקרים הבאים:
- המאזין רוצה להפעיל את אותה לוגיקה עבור כמה אירועים. לדוגמה, עדכון ממשק משתמש גם ל-
onPlaybackStateChangedוגם ל-onPlayWhenReadyChanged. - ל-listener צריכה להיות גישה לאובייקט
Playerכדי להפעיל אירועים נוספים, למשל מעבר למיקום מסוים אחרי מעבר בין פריטי מדיה. - המאזין מתכוון להשתמש בכמה ערכי מצב שמדווחים באמצעות קריאות חוזרות נפרדות, או בשילוב עם
Playerשיטות getter. לדוגמה, השימוש ב-Player.getCurrentWindowIndex()עםTimelineשסופק ב-onTimelineChangedבטוח רק מתוך הקריאה החוזרת (callback) שלonEvents. - הפונקציה event listener מתעניינת בשאלה אם האירועים התרחשו יחד באופן לוגי.
לדוגמה,
onPlaybackStateChangedל-STATE_BUFFERINGבגלל מעבר בין פריטי מדיה.
במקרים מסוימים, יכול להיות שהמאזינים יצטרכו לשלב את הקריאות החוזרות הנפרדות עם הקריאה החוזרת onEvents הגנרית, למשל כדי לתעד את הסיבות לשינוי פריט המדיה באמצעות onMediaItemTransition, אבל רק אחרי שכל השינויים במצב יהיו זמינים לשימוש ביחד ב-onEvents.
משתמש ב-AnalyticsListener
כשמשתמשים ב-ExoPlayer, אפשר לרשום AnalyticsListener אצל השחקן על ידי קריאה ל-addAnalyticsListener. הטמעות של AnalyticsListener יכולות להאזין לאירועים מפורטים שעשויים להיות שימושיים למטרות ניתוח ורישום ביומן. פרטים נוספים זמינים בדף Analytics.
משתמש ב-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();