יצירת אפליקציה בסיסית של נגן מדיה באמצעות Media3 ExoPlayer

Jetpack Media3 מגדיר ממשק Player שמתאר את הפונקציונליות הבסיסית להפעלה של קובצי וידאו ואודיו. הטמעת ברירת המחדל היא ExoPlayer בממשק הזה ב-Media3. מומלץ להשתמש ב-ExoPlayer, מכיוון שהוא מספק קבוצה מקיפה של תכונות שמכסות את רוב תרחישי השימוש של ההפעלה, ניתנת להתאמה אישית כדי לטפל בכל תרחישי השימוש הנוספים. גם ExoPlayer מפשט את הפיצול של המכשירים ושל מערכות ההפעלה כדי שהקוד יפעל באופן עקבי בכל הסביבה העסקית של Android. ExoPlayer כולל:

הדף הזה מסביר כמה מהשלבים העיקריים ביצירת הפעלה לפרטים נוספים, אפשר לעיין במדריכים המלאים Media3 ExoPlayer.

תחילת העבודה

כדי להתחיל, צריך להוסיף תלות ב-exoPlayer, בממשק המשתמש ובמודולים נפוצים של Jetpack Media3:

implementation "androidx.media3:media3-exoplayer:1.4.1"
implementation "androidx.media3:media3-ui:1.4.1"
implementation "androidx.media3:media3-common:1.4.1"

בהתאם לתרחיש לדוגמה שלכם, ייתכן שתצטרכו גם מודולים נוספים מ-Media3, כמו exoplayer-dash כדי להפעיל שידורים בפורמט DASH.

חשוב להחליף את 1.4.1 בגרסה המועדפת של לספרייה. אפשר לעיין בנתוני הגרסה כדי לראות את הגרסה העדכנית ביותר.

יצירת נגן מדיה

באמצעות Media3, אפשר להשתמש ביישום המצורף של Player ExoPlayer, או ליצור הטמעה מותאמת אישית משלכם.

יצירת ExoPlayer

הדרך הפשוטה ביותר ליצור מכונה של ExoPlayer היא:

Kotlin

val player = ExoPlayer.Builder(context).build()

Java

ExoPlayer player = new ExoPlayer.Builder(context).build();

אפשר ליצור נגן מדיה בשיטה onCreate() של מחזור החיים Activity, Fragment או Service שבהם הוא נמצא.

Builder כולל מגוון אפשרויות להתאמה אישית שעשויות לעניין אתכם, כמו:

Media3 מספקת רכיב ממשק משתמש PlayerView שניתן לכלול . הרכיב הזה כולל PlayerControlView להפעלה הלחצנים, SubtitleView להצגת כתוביות, ו-Surface לעיבוד וידאו.

הנגן בהכנה

הוספת פריטי מדיה לפלייליסט עבור בהפעלה באמצעות שיטות כמו setMediaItem() ו-addMediaItem(). לאחר מכן, צריך להתקשר למספר prepare() כדי להתחיל לטעון מדיה ולהשיג את המשאבים הדרושים.

אין לבצע את השלבים האלה לפני שהאפליקציה פועלת בחזית. אם השחקן נמצא ב-Activity או Fragment, המשמעות היא שהוא מכין את הנגן שיטה onStart() של מחזור החיים ברמת API 24 ומעלה או onResume() ה-method של מחזור החיים ברמת API 23 ומטה. לגבי שחקן שנכלל ב-Service: אפשר להכין אותה בonCreate().

שליטה בנגן

לאחר שהנגן מוכן, אפשר לשלוט בהפעלה באמצעות שיטות קריאה בנגן, למשל:

רכיבי ממשק משתמש כמו PlayerView או PlayerControlView יתעדכנו בהתאם לשחקן.

שחרור הנגן

כדי להפעיל את הסרטון, עשויים להיות משאבים מוגבלים, כמו וידאו ולכן חשוב לקרוא ל-release() בנגן שלכם כדי לפנות משאבים כשהשחקן כבר לא נחוץ.

אם הנגן שלך נמצא בגרסה Activity או Fragment, משחררים אותו שיטה onStop() של מחזור החיים ברמת API 24 ומעלה או onPause() ברמת API 23 ומטה. לגבי שחקן שנכלל ב-Service, אפשר להשיק אותו ב-onDestroy().

ניהול הפעלה עם הפעלת מדיה

ב-Android, סשנים במדיה מספקים דרך סטנדרטית לאינטראקציה עם מדיה את השחקנים על פני גבולות התהליכים. חיבור של סשן מדיה לנגן שלך מאפשר לפרסם את הפעלת המדיה באופן חיצוני ולקבל את ההפעלה פקודות ממקורות חיצוניים, למשל כדי לשלב פקדי מדיה של המערכת בנייד ובגדול מכשירים במסך.

כדי להשתמש בסשנים של מדיה, צריך להוסיף תלות במודול Media3 סשן:

implementation "androidx.media3:media3-session:1.4.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:

<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 כדי לקשר אותם יחד. בשיטה 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 כשהוא כבר לא פעיל נדרש, כמו ה-method onStop() של מחזור החיים של Activity, באמצעות קריאה MediaController.releaseFuture()

פרסום התראה

שירותים שפועלים בחזית נדרשים לפרסם התראה בזמן הפעילות. א' מערכת MediaSessionService תיצור באופן אוטומטי התראה אחת (MediaStyle) לגבי אותך בפורמט של MediaNotification. כדי לספק התראה בהתאמה אישית, צריך ליצור MediaNotification.Provider עם DefaultMediaNotificationProvider.Builder או על ידי יצירה של ממשק ספק בהתאמה אישית. הוספה של הספק של MediaSession באמצעות setMediaNotificationProvider.

פרסום ספריית התוכן שלכם

MediaLibraryService מתבסס על MediaSessionService באמצעות מתן הרשאה ללקוח כדי לדפדף בתוכן המדיה שסופק על ידי האפליקציה. אפליקציות לקוח מטמיעות MediaBrowser כדי לבצע אינטראקציה עם MediaLibraryService.

הטמעה של MediaLibraryService דומה להטמעת MediaSessionService, חוץ מזה שב-onGetSession() צריך להחזיר MediaLibrarySession במקום MediaSession. בהשוואה ל- MediaSession.Callback, MediaLibrarySession.Callback כולל עוד שיטות שמאפשרות ללקוח לנווט בתוכן שמוצע על ידי שירות ספריות.

בדומה לMediaSessionService, צריך להצהיר על MediaLibraryService ב מניפסט ולבקש את ההרשאה FOREGROUND_SERVICE כדי להפעיל חזית 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" />

הדוגמה שלמעלה כוללת מסנן Intent עבור MediaLibraryService ולתאימות לאחור, גם הגרסה הקודמת של MediaBrowserService. מסנן Intent נוסף מאפשר אפליקציות לקוח באמצעות ה-API MediaBrowserCompat לזהות את Service.

MediaLibrarySession מאפשר להציג את ספריית התוכן בעץ עם שורש אחד, MediaItem. כל MediaItem בעץ יכול להכיל כל מספר של צומתי MediaItem צאצאים. אפשר למלא שורש אחר, עץ שונה, בהתאם לבקשה של אפליקציית הלקוח. לדוגמה, העץ לחזור ללקוח שמחפש רשימה של פריטי מדיה מומלצים, מכילים את השורש MediaItem ורמה אחת של צמתים מסוג MediaItem צאצאים, ואילו העץ שתחזירו לאפליקציית לקוח אחרת עשוי לייצג את ספריית התוכן המלאה.

יצירת MediaLibrarySession

MediaLibrarySession מרחיב את ממשק ה-API של MediaSession כדי להוסיף ממשקי API לגלישה בתוכן. בהשוואה ל- MediaSession התקשרות חזרה, הקריאה החוזרת MediaLibrarySession מוסיפה שיטות כמו:

  • onGetLibraryRoot() למקרים שבהם לקוח מבקש את הרמה הבסיסית (root) MediaItem של עץ תוכן
  • onGetChildren() למקרים שבהם לקוח מבקש מהצאצאים של MediaItem בעץ התוכן
  • onGetSearchResult() למקרים שבהם לקוח מבקש תוצאות חיפוש מעץ התוכן שאילתה

שיטות רלוונטיות להתקשרות חזרה יכללו LibraryParams אובייקט עם אותות נוספים לגבי סוג עץ התוכן שאפליקציית לקוח שבו הוא מתעניין.