פעילויות מדיה מספקות דרך אוניברסלית לאינטראקציה עם אודיו או וידאו
נגן. ב-Media3, הנגן שמוגדר כברירת מחדל הוא המחלקה ExoPlayer
, שמטמיעה
הממשק Player
. חיבור של סשן המדיה לנגן מאפשר לאפליקציה
כדי לפרסם הפעלת מדיה באופן חיצוני ולקבל פקודות הפעלה מ
ממקורות חיצוניים.
מקור הפקודות עשוי להיות לחצנים פיזיים, כמו לחצן ההפעלה של האוזניות או השלט הרחוק של הטלוויזיה. הן עשויות להגיע גם מאפליקציות לקוח שיש להן בקר מדיה, למשל להורות ל"השהיה" ל-Google Assistant. המדיה מקצה את הפקודות האלה לנגן של אפליקציית המדיה.
מתי כדאי לבחור סשן מדיה
כשמטמיעים את MediaSession
, מאפשרים למשתמשים לשלוט בהפעלה:
- באמצעות האוזניות שלהם. לעיתים קרובות יש לחצנים או אינטראקציות מגע המשתמש יכול לבצע באוזניות כדי להפעיל או להשהות מדיה, או לעבור אל או את הטראק הקודם.
- לדבר אל Google Assistant. דפוס נפוץ הוא לומר "OK 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 Assistant. באותו אופן, ייתכן שתרצה להעניק גישה ל-Android כדי לאפשר שליטה בהודעות ובמסך הנעילה, או ב-Wear OS כדי לשלוט בהפעלה מתצוגת השעון. לקוחות חיצוניים יכולים משתמשים בבקר מדיה כדי לשלוח פקודות הפעלה לאפליקציית המדיה. הנושאים האלה שהתקבלו בפעילות המדיה שלכם, שבסופו של דבר מקצה פקודות נגן מדיה.
כאשר בקר עומד להתחבר לסשן המדיה,
onConnect()
נקראת. ניתן להשתמש ב-ControllerInfo
שסופק
כדי להחליט אם לאשר
או דחייה
לבקשה. אפשר לראות דוגמה לאישור בקשת חיבור בהצהרה
רשימת הפקודות הזמינות.
לאחר ההתחברות, בקר יכול לשלוח פקודות הפעלה לסשן.
ואז נתן את הפקודות האלה לנגן. הפעלה ופלייליסט
בפקודות שמוגדרות בממשק של Player
מטפלים באופן אוטומטי
סשן.
שיטות אחרות של קריאה חוזרת מאפשרות לכם לטפל, למשל, בבקשות
פקודות הפעלה מותאמות אישית
שינוי הפלייליסט).
הקריאות החוזרות האלה כוללות באופן דומה אובייקט ControllerInfo
, כך שניתן לשנות
האופן שבו אתם מגיבים לכל בקשה בנפרד לנאמני מידע.
עריכת הפלייליסט
סשן מדיה יכול לשנות ישירות את הפלייליסט של הנגן שלו, כמו שמוסבר
ה
מדריך ExoPlayer לפלייליסטים.
הבקרים יוכלו גם לשנות את הפלייליסט במקרים הבאים:
COMMAND_SET_MEDIA_ITEM
או COMMAND_CHANGE_MEDIA_ITEMS
זמינה לנאמני מידע.
כשמוסיפים פריטים חדשים לפלייליסט, בדרך כלל לנגן נדרש MediaItem
מופיע עם
URI מוגדר
כדי שתהיה אפשרות להפעיל אותן. כברירת מחדל, פריטים חדשים שנוספו מועברים באופן אוטומטי
ל-methods כמו player.addMediaItem
, אם יש להם URI מוגדר.
כדי להתאים אישית את המופעים של MediaItem
שנוספו לנגן, אפשר:
לשנות
onAddMediaItems()
.
השלב הזה נדרש אם רוצים לתמוך בנאמני מידע שמבקשים מדיה
ללא URI מוגדר. במקום זאת, MediaItem
בדרך כלל כולל
אחד או יותר מהשדות הבאים מוגדרים כדי לתאר את המדיה המבוקשת:
MediaItem.id
: מזהה גנרי שמזהה את המדיה.MediaItem.RequestMetadata.mediaUri
: URI של בקשה שיכול להשתמש ב- ולא בהכרח ניתנים להפעלה ישירות על ידי הנגן.MediaItem.RequestMetadata.searchQuery
: שאילתת חיפוש בטקסט, לדוגמה מ-Google Assistant.MediaItem.MediaMetadata
: מטא-נתונים מובְנים כמו 'title' או 'אמן'.
לאפשרויות נוספות של התאמה אישית עבור פלייליסטים חדשים לגמרי, ניתן
לשנות גם את
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
methods. כך אפשר להתאים אישית את העיצוב של האפליקציה
בתגובה לפקודה מסוימת, אם המקור שלה הוא המערכת,
את האפליקציה שלכם או אפליקציות לקוח אחרות.
עדכון פריסה מותאמת אישית לאחר אינטראקציה של משתמש
אחרי טיפול בפקודה מותאמת אישית או באינטראקציה אחרת עם הנגן שלכם,
יכול להיות שתרצו לעדכן את הפריסה שמוצגת בממשק המשתמש של הבקר. דוגמה אופיינית
הוא לחצן החלפת מצב שמשנה את הסמל שלו לאחר שהוא מפעיל את הפעולה המשויכת
באמצעות הלחצן הזה. כדי לעדכן את הפריסה, אפשר להשתמש ב
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 ב-
התאמה אישית.
זיהוי הבקר ששולח בקשה לפקודת נגן
כשקריאה ל-method 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 ובציוד היקפי אחר
מכשירים, למשל לחצן ההפעלה/ההשהיה באוזניות Bluetooth. נקודות אחיזה של Media3
אירועי מדיה שיופיעו עבורך כשהם מגיעים לסשן ומתקשרים
השיטה Player
המתאימה בנגן של הסשן.
האפליקציה יכולה לשנות את התנהגות ברירת המחדל על ידי שינוי
MediaSession.Callback.onMediaButtonEvent(Intent)
במקרה כזה, האפליקציה
יכול/צריך לטפל בכל הפרטים הספציפיים של API בפני עצמו.
טיפול בשגיאות ודיווח עליהן
יש שני סוגי שגיאות שסשן פולט ומדווח לנאמני מידע. שגיאות חמורות מדווחים על כשל טכני בהפעלה של סשן בנגן שמפריע להפעלה. שגיאות חמורות מדווחות לנאמני המידע באופן אוטומטי כשהם מתרחשים. שגיאות לא חמורות הן לא טכניות או קשורות למדיניות שגיאות שלא מפריעות להפעלה ונשלחות לבקרים על ידי ידנית.
שגיאות הפעלה חמורות
שגיאת הפעלה חמורה מדווחת לסשן על ידי הנגן, ולאחר מכן
דווחו לנאמני מידע לבצע קריאה באמצעות
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
לנגן בפועל
ומיירט קריאות חוזרות (callback) שמדווחות על שגיאה. התאמה אישית
לאחר מכן, 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. } });