ממשק הנגן

נגן הוא הרכיב באפליקציה שמאפשר הפעלה של פריטי מדיה. ממשק Media3‏ Player מגדיר סקירה כללית של הפונקציונליות שבדרך כלל מטופלת על ידי נגן. הנתונים האלה כוללים:

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

Media3 מספק גם הטמעה של הממשק Player, שנקראת ExoPlayer.

ממשק משותף בין הרכיבים

כמה רכיבים ב-Media3 מיישמים את ממשק הנגן, לדוגמה:

רכיב תיאור והערות לגבי התנהגות
ExoPlayer ממשק API לנגן מדיה והטמעת ברירת המחדל של הממשק Player.
MediaController מתקשר עם MediaSession כדי לשלוח פקודות הפעלה. אם Player ו-MediaSession נמצאים ב-Service נפרד מ-Activity או מ-Fragment שבהם נמצא ממשק המשתמש של הנגן, אפשר להקצות את MediaController כנגן של ממשק המשתמש PlayerView. קריאות ל-method של הפעלה ולפלייליסטים נשלחות ל-Player דרך ה-MediaSession.
MediaBrowser בנוסף לפונקציונליות שמציע MediaController, הוא יוצר אינטראקציה עם MediaLibrarySession כדי לעיין בתוכן המדיה שזמין.
SimpleBasePlayer הטמעה של Player שמפחיתה את מספר השיטות שצריך להטמיע למינימום. שימושי כשמשתמשים בנגן מותאם אישית שרוצים לקשר ל-MediaSession.
ForwardingSimpleBasePlayer תת-סוג של SimpleBasePlayer שמיועד להעביר פעולות של הפעלה ל-Player אחר, תוך שמירה על אותן התאמות אישיות עקביות של התנהגות כמו ב-SimpleBasePlayer. אפשר להשתמש בכיתה הזו כדי לדכא או לשנות פעולות הפעלה ספציפיות.
CastPlayer הטמעה של Player שמתקשרת עם אפליקציית מקלט Cast. ההתנהגות תלויה בסשן Cast הבסיסי.

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

ארכיטקטורת ההפעלה של Media3

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

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

תרשים שבו מוצג איך רכיבי ההפעלה של Media3 משתלבים בארכיטקטורה של אפליקציית מדיה.
איור 1: ממשק Player ממלא תפקיד מרכזי בארכיטקטורה של Media3.

מצב הנגן

המצב של נגן מדיה שמטמיע את הממשק Player מורכב בעיקר מ-4 קטגוריות של מידע:

  1. מצב ההפעלה
  2. פלייליסט של פריטי מדיה
  3. מאפייני הפעלה/השהיה, כמו:
    • playWhenReady: אינדיקציה לכך שהמשתמש רוצה שהמדיה תופעל כשהדבר אפשרי או שתישאר מושהית
    • Playback suppression reason: אינדיקציה לסיבת ההשתקה של ההפעלה, אם רלוונטי, גם אם הערך של playWhenReady הוא true
    • isPlaying: אינדיקציה לכך שהנגן פועל כרגע. הערך true יופיע רק אם סטטוס ההפעלה הוא STATE_READY, הערך של playWhenReady הוא true וההפעלה לא מושבתת.
  4. מיקום ההפעלה, כולל:

בנוסף, הממשק Player מאפשר גישה לטראקים הזמינים, למטא-נתונים של המדיה, למהירות ההפעלה, לנפח ולמאפיינים משניים אחרים של ההפעלה.

האזנה לשינויים

משתמשים ב-Player.Listener כדי להאזין לשינויים ב-Player. במאמר אירועי Player במסמכי העזרה של ExoPlayer מוסבר איך יוצרים מאזין ומשתמשים בו.

חשוב לזכור שממשק המאזין לא כולל קריאות חזרה (callbacks) למעקב אחרי התקדמות רגילה של ההפעלה. כדי לעקוב באופן רציף אחרי התקדמות ההפעלה, למשל כדי להגדיר ממשק משתמש של סרגל התקדמות, צריך לשלוח שאילתה לגבי המיקום הנוכחי במרווחי זמן מתאימים.

Kotlin

val handler = Handler(Looper.getMainLooper())
fun checkPlaybackPosition(delayMs: Long): Boolean =
  handler.postDelayed(
    {
      val currentPosition = player.currentPosition
      // Update UI based on currentPosition
      checkPlaybackPosition(delayMs)
    },
    delayMs)

Java

Handler handler = new Handler(Looper.getMainLooper());
boolean checkPlaybackPosition(long delayMs) {
    return handler.postDelayed(() -> {
        long currentPosition = player.getCurrentPosition();
        // Update UI based on currentPosition
        checkPlaybackPosition(delayMs);
    }, delayMs);
}

שליטה בהפעלה

בממשק Player יש הרבה דרכים לשנות את המצב ולשלוט בהפעלה:

הטמעות בהתאמה אישית של Player

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

מתחילים על ידי שינוי השיטה getState(). כשמפעילים את השיטה הזו, היא אמורה לאכלס את המצב הנוכחי של השחקן, כולל:

  • קבוצת הפקודות הזמינות
  • מאפייני ההפעלה, כמו אם הנגן צריך להתחיל להפעיל כשמצב ההפעלה הוא STATE_READY, המדד של פריט המדיה שפועל כרגע ומיקום ההפעלה בתוך הפריט הנוכחי

Kotlin

class CustomPlayer : SimpleBasePlayer(looper) {
  override fun getState(): State {
    return State.Builder()
      .setAvailableCommands(...) // Set which playback commands the player can handle
      // Configure additional playback properties
      .setPlayWhenReady(true, PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST)
      .setCurrentMediaItemIndex(0)
      .setContentPositionMs(0)
      .build()
  }
}

Java

public class CustomPlayer extends SimpleBasePlayer {
  public CustomPlayer(Looper looper) {
    super(looper);
  }

  @Override
  protected State getState() {
    return new State.Builder()
      .setAvailableCommands(...) // Set which playback commands the player can handle
      // Configure additional playback properties
      .setPlayWhenReady(true, PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST)
      .setCurrentMediaItemIndex(0)
      .setContentPositionMs(0)
      .build();
  }
}

SimpleBasePlayer יאלץ את State להיווצר עם שילוב תקין של ערכי המצב. הוא גם יטפל במאזינים ויודיע להם על שינויים במצב. אם צריך להפעיל עדכון מצב באופן ידני, צריך להתקשר למספר invalidateState().

מלבד השיטה getState(), צריך להטמיע רק שיטות שמשמשות לפקודות שהנגן מצהיר שהן זמינות. מאתרים את שיטת הטיפול שניתנת לשינוי שתואמת לפונקציונליות שרוצים להטמיע. לדוגמה, אפשר לשנות את השיטה handleSeek() כדי לתמוך בפעולות כמו COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM ו-COMMAND_SEEK_TO_NEXT_MEDIA_ITEM.