Jetpack Media3 מגדיר ממשק Player
שמתווה פונקציונליות בסיסית להפעלה של קובצי וידאו ואודיו. ExoPlayer
היא הטמעה שמוגדרת כברירת מחדל של הממשק הזה ב-Media3. מומלץ להשתמש ב-ExoPlayer, כי הוא מספק קבוצה מקיפה של תכונות שמתאימות לרוב תרחישי השימוש בהפעלה, ואפשר להתאים אותו לכל תרחיש שימוש נוסף שיכול להיות לכם. בנוסף, ExoPlayer מסתיר את הפיצול של המכשירים ומערכות ההפעלה, כך שהקוד פועל באופן עקבי בכל האקוסיסטם של Android. ExoPlayer כולל:
- תמיכה בפלייליסטים
- תמיכה במגוון פורמטים של סטרימינג פרוגרסיבי ואדפטיבי formats
- תמיכה בהוספת מודעות גם בצד הלקוח וגם בצד השרת
- תמיכה בהפעלה של תוכן שמוגן על ידי DRM
בדף הזה מפורטים כמה מהשלבים העיקריים ליצירת אפליקציית הפעלה. לפרטים נוספים אפשר לעיין במדריכים המלאים שלנו בנושא Media3 ExoPlayer.
תחילת העבודה
כדי להתחיל, מוסיפים תלות במודולים ExoPlayer, UI ו-Common של Jetpack Media3:
implementation "androidx.media3:media3-exoplayer:1.7.1" implementation "androidx.media3:media3-ui:1.7.1" implementation "androidx.media3:media3-common:1.7.1"
בהתאם לתרחיש השימוש, יכול להיות שתצטרכו גם מודולים נוספים מ-Media3, כמו exoplayer-dash
כדי להפעיל סטרימינג בפורמט DASH.
חשוב להחליף את 1.7.1
בגרסה המועדפת של הספרייה. אפשר לעיין בהערות לגבי הגרסה כדי לראות את הגרסה האחרונה.
יצירת נגן מדיה
באמצעות Media3, אפשר להשתמש בהטמעה הכלולה של Player
הממשק, ExoPlayer
, או ליצור הטמעה מותאמת אישית משלכם.
יצירת ExoPlayer
הדרך הפשוטה ביותר ליצור מופע ExoPlayer
היא כדלקמן:
Kotlin
val player = ExoPlayer.Builder(context).build()
Java
ExoPlayer player = new ExoPlayer.Builder(context).build();
אפשר ליצור את נגן המדיה ב-lifecycle method onCreate()
של Activity
, Fragment
או Service
שבהם הוא נמצא.
האפשרות Builder
כוללת מגוון אפשרויות להתאמה אישית שעשויות לעניין אתכם, כמו:
-
setAudioAttributes()
כדי להגדיר את הטיפול במיקוד אודיו setHandleAudioBecomingNoisy()
כדי להגדיר את אופן הפעולה של ההפעלה כשמנתקים מכשיר להשמעת אודיוsetTrackSelector()
כדי להגדיר בחירת טראקים
Media3 מספקת רכיב ממשק משתמש PlayerView
שאפשר לכלול בקובץ הפריסה של האפליקציה. הרכיב הזה כולל PlayerControlView
להפעלת אמצעי בקרה, SubtitleView
להצגת כתוביות ו-Surface
לעיבוד סרטונים.
הכנת הנגן
להוסיף פריטי מדיה לפלייליסט להפעלה באמצעות שיטות כמו setMediaItem()
ו-addMediaItem()
.
לאחר מכן, קוראים ל-prepare()
כדי להתחיל לטעון מדיה ולקבל את המשאבים הנדרשים.
לא מומלץ לבצע את השלבים האלה לפני שהאפליקציה עוברת לחזית. אם נגן המדיה נמצא במצב Activity
או Fragment
, צריך להכין את הנגן בשיטת מחזור החיים onStart()
ברמת API 24 ומעלה, או בשיטת מחזור החיים onResume()
ברמת API 23 ומטה. אם השחקן נמצא בService
, אפשר להכין אותו בonCreate()
.
שליטה בנגן
אחרי שהנגן מוכן, אפשר לשלוט בהפעלה באמצעות קריאה לשיטות בנגן, כמו:
-
play()
ו-pause()
כדי להתחיל ולהשהות את ההפעלה -
seekTo()
כדי לדלג למיקום מסוים בפריט המדיה הנוכחי -
seekToNextMediaItem()
ו-seekToPreviousMediaItem()
כדי לנווט בפלייליסט
רכיבי ממשק משתמש כמו PlayerView
או PlayerControlView
יתעדכנו בהתאם כשהם מקושרים לנגן.
הפעלת הנגן
הפעלת הסרטון עשויה לדרוש משאבים שזמינים בכמות מוגבלת, כמו מפענחי וידאו, ולכן חשוב להפעיל את release()
בנגן כדי לפנות משאבים כשהנגן כבר לא נחוץ.
אם נגן המדיה נמצא במצב Activity
או Fragment
, צריך לשחרר את הנגן בשיטת מחזור החיים onStop()
ברמת API 24 ומעלה, או בשיטה onPause()
ברמת API 23 ומטה. אם שחקן נמצא בService
, אפשר לשחרר אותו בonDestroy()
.
ניהול ההפעלה באמצעות סשן מדיה
ב-Android, סשנים של מדיה מספקים דרך סטנדרטית ליצור אינטראקציה עם נגן מדיה מעבר לגבולות התהליך. חיבור של סשן מדיה לנגן מאפשר לפרסם את הפעלת המדיה באופן חיצוני ולקבל פקודות הפעלה ממקורות חיצוניים, למשל כדי לבצע שילוב עם פקדי מדיה של המערכת במכשירים ניידים ובמסכים גדולים.
כדי להשתמש בסשנים של מדיה, צריך להוסיף תלות במודול Session של Media3:
implementation "androidx.media3:media3-session:1.7.1"
יצירת סשן מדיה
אפשר ליצור MediaSession
אחרי הפעלת נגן באופן הבא:
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 מסנכרן אוטומטית את המצב של Player
עם המצב של MediaSession
. השיטה הזו פועלת עם כל הטמעה של Player
, כולל ExoPlayer
, CastPlayer
או הטמעה בהתאמה אישית.
מתן שליטה ללקוחות אחרים
אפליקציות לקוח יכולות להטמיע בקר מדיה כדי לשלוט בהפעלה של סשן המדיה. כדי לקבל את הבקשות האלה, צריך להגדיר אובייקט callback כשיוצרים את MediaSession
.
כשבקר עומד להתחבר להפעלת המדיה, מתבצעת קריאה לשיטה
onConnect()
. אפשר להשתמש בControllerInfo
שסופק כדי להחליט אם לאשר או לדחות את הבקשה. אפשר לראות דוגמה לכך באפליקציית ההדגמה של Media3 Session.
אחרי החיבור, הבקר יכול לשלוח פקודות הפעלה להפעלה של הסרטון. לאחר מכן, ההפעלה מעבירה את הפקודות האלה לנגן. הפעלת פקודות ופקודות של פלייליסט שמוגדרות בממשק Player
מטופלות באופן אוטומטי על ידי הסשן.
שיטות אחרות של קריאה חוזרת מאפשרות לכם לטפל, למשל, בבקשות לפקודות הפעלה בהתאמה אישית ובשינוי הפלייליסט. גם הקריאות החוזרות האלה כוללות אובייקט ControllerInfo
, כך שתוכלו לקבוע את בקרת הגישה לכל בקשה בנפרד.
הפעלת מדיה ברקע
כדי להמשיך להפעיל מדיה כשהאפליקציה לא בחזית, למשל כדי להפעיל מוזיקה, ספרי אודיו או פודקאסטים גם כשהמשתמש לא פתח את האפליקציה, צריך להוסיף את Player
ו-MediaSession
לשירות בחזית. Media3 מספק את הממשק MediaSessionService
למטרה הזו.
הטמעה של MediaSessionService
יוצרים מחלקה שמרחיבה את MediaSessionService
ויוצרים מופע של MediaSession
בשיטת מחזור החיים onCreate()
.
Kotlin
class PlaybackService : MediaSessionService() { private var mediaSession: MediaSession? = null // Create your Player and MediaSession in the onCreate lifecycle event override fun onCreate() { super.onCreate() val player = ExoPlayer.Builder(this).build() mediaSession = MediaSession.Builder(this, player).build() } // Remember to release the player and media session in onDestroy override fun onDestroy() { mediaSession?.run { player.release() release() mediaSession = null } super.onDestroy() } }
Java
public class PlaybackService extends MediaSessionService { private MediaSession mediaSession = null; @Override public void onCreate() { super.onCreate(); ExoPlayer player = new ExoPlayer.Builder(this).build(); mediaSession = new MediaSession.Builder(this, player).build(); } @Override public void onDestroy() { mediaSession.getPlayer().release(); mediaSession.release(); mediaSession = null; super.onDestroy(); } }
במניפסט, מגדירים את המחלקה Service
עם מסנן Intent MediaSessionService
ומבקשים את ההרשאה FOREGROUND_SERVICE
להפעלת שירות בחזית:
<service
android:name=".PlaybackService"
android:foregroundServiceType="mediaPlayback"
android:exported="true">
<intent-filter>
<action android:name="androidx.media3.session.MediaSessionService"/>
</intent-filter>
</service>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
לבסוף, בכיתה שיצרתם, מבטלים את השיטה onGetSession()
כדי לשלוט בגישת הלקוח לסשן המדיה. מחזירים MediaSession
כדי לאשר את הבקשה לחיבור, או מחזירים null
כדי לדחות את הבקשה.
Kotlin
// This example always accepts the connection request override fun onGetSession( controllerInfo: MediaSession.ControllerInfo ): MediaSession? = mediaSession
Java
@Override public MediaSession onGetSession(MediaSession.ControllerInfo controllerInfo) { // This example always accepts the connection request return mediaSession; }
מתחברים לממשק המשתמש
עכשיו סשן המדיה שלכם נמצא בService
בנפרד מActivity
או מFragment
שבהם נמצא ממשק המשתמש של הנגן, ואתם יכולים להשתמש בMediaController
כדי לקשר ביניהם. ב-method onStart()
של Activity
או Fragment
עם ממשק המשתמש שלכם, יוצרים SessionToken
בשביל MediaSession
, ואז משתמשים ב-SessionToken
כדי ליצור MediaController
. הבנייה של MediaController
מתבצעת באופן אסינכרוני.
Kotlin
override fun onStart() { val sessionToken = SessionToken(this, ComponentName(this, PlaybackService::class.java)) val controllerFuture = MediaController.Builder(this, sessionToken).buildAsync() controllerFuture.addListener( { // Call controllerFuture.get() to retrieve the MediaController. // MediaController implements the Player interface, so it can be // attached to the PlayerView UI component. playerView.setPlayer(controllerFuture.get()) }, MoreExecutors.directExecutor() ) }
Java
@Override public void onStart() { SessionToken sessionToken = new SessionToken(this, new ComponentName(this, PlaybackService.class)); ListenableFuture<MediaController> controllerFuture = new MediaController.Builder(this, sessionToken).buildAsync(); controllerFuture.addListener(() -> { // Call controllerFuture.get() to retrieve the MediaController. // MediaController implements the Player interface, so it can be // attached to the PlayerView UI component. playerView.setPlayer(controllerFuture.get()); }, MoreExecutors.directExecutor()) }
MediaController
מטמיע את הממשק Player
, כך שאפשר להשתמש באותן שיטות כמו play()
ו-pause()
כדי לשלוט בהפעלה. בדומה לרכיבים אחרים, חשוב לזכור לשחרר את MediaController
כשאין בו יותר צורך, למשל בשיטת מחזור החיים onStop()
של Activity
, על ידי קריאה ל-MediaController.releaseFuture()
.
פרסום התראה
כדי לפרסם התראה בזמן שהאפליקציה פעילה, צריך להשתמש בשירותים בחזית. מערכת
MediaSessionService
תיצור באופן אוטומטי התראה בצורת MediaNotification
.MediaStyle
כדי לספק התראה מותאמת אישית, יוצרים MediaNotification.Provider
עם DefaultMediaNotificationProvider.Builder
או יוצרים הטמעה מותאמת אישית של ממשק הספק. מוסיפים את הספק ל-MediaSession
באמצעות setMediaNotificationProvider
.
פרסום ספריית התוכן
MediaLibraryService
מבוסס על MediaSessionService
ומאפשר לאפליקציות לקוח לעיין בתוכן המדיה שמסופק על ידי האפליקציה שלכם. אפליקציות לקוח מטמיעות MediaBrowser
כדי ליצור אינטראקציה עם MediaLibraryService
.
הטמעה של MediaLibraryService
דומה להטמעה של MediaSessionService
, אבל ב-onGetSession()
צריך להחזיר MediaLibrarySession
במקום MediaSession
. בהשוואה ל-MediaSession.Callback
, MediaLibrarySession.Callback
כולל שיטות נוספות שמאפשרות ללקוח דפדפן לנווט בתוכן שמוצע על ידי שירות הספרייה שלכם.
בדומה ל-MediaSessionService
, צריך להצהיר על MediaLibraryService
במניפסט ולבקש את ההרשאה FOREGROUND_SERVICE
כדי להריץ שירות שפועל בחזית:
<service
android:name=".PlaybackService"
android:foregroundServiceType="mediaPlayback"
android:exported="true">
<intent-filter>
<action android:name="androidx.media3.session.MediaLibraryService"/>
<action android:name="android.media.browse.MediaBrowserService"/>
</intent-filter>
</service>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
בדוגמה שלמעלה יש מסנן כוונות גם ל-MediaLibraryService
וגם ל-MediaBrowserService
מדור קודם, לצורך תאימות לאחור. מסנן הכוונות הנוסף מאפשר לאפליקציות לקוח שמשתמשות ב-API MediaBrowserCompat
לזהות את Service
.
MediaLibrarySession
מאפשרת להציג את ספריית התוכן במבנה היררכי, עם שורש יחיד MediaItem
. לכל MediaItem
בעץ יכולים להיות מספר כלשהו של צאצאים, שהם צמתי MediaItem
. אפשר להציג שורש אחר או עץ אחר בהתאם לבקשה של אפליקציית הלקוח. לדוגמה, העץ שמוחזר ללקוח שמחפש רשימה של פריטי מדיה מומלצים עשוי להכיל רק את שורש MediaItem
ורמה אחת של צאצאים MediaItem
, בעוד שהעץ שמוחזר לאפליקציית לקוח אחרת עשוי לייצג ספרייה מלאה יותר של תוכן.
יצירת MediaLibrarySession
MediaLibrarySession
מרחיב את MediaSession
API כדי להוסיף ממשקי API לעיון בתוכן. בהשוואה לMediaSession
callback, ב-MediaLibrarySession
callback יש שיטות נוספות כמו:
onGetLibraryRoot()
אם לקוח מבקש את שורשMediaItem
של עץ תוכן-
onGetChildren()
אם לקוח מבקש את צאצאיMediaItem
בעץ התוכן onGetSearchResult()
כשלקוח מבקש תוצאות חיפוש מעץ התוכן עבור שאילתה מסוימת
שיטות רלוונטיות של קריאה חוזרת יכללו אובייקט LibraryParams
עם אותות נוספים לגבי סוג עץ התוכן שאפליקציית לקוח מתעניינת בו.