ממשק הנגן

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

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

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

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

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

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

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

ארכיטקטורת הפעלת מדיה 3

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

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

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

מצב הנגן

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

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

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

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

שימוש ב-Player.Listener כדי להאזין לשינויים ב-Player. אפשר לעיין בתיעוד של ExoPlayer בכתובת אירועי שחקן עבור פרטים על יצירת מאזינים ושימוש בהם.

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

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 כדי לצמצם למינימום את מספר השיטות שצריך ליישם.

כדי להתחיל, צריך לשנות את ה-method 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().

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