מדריך להעברת נתונים מ-AndroidX Media3

אפליקציות שמשתמשות כרגע בספרייה העצמאית com.google.android.exoplayer2 וב-androidx.media צריכות לעבור ל-androidx.media3. משתמשים בסקריפט ההעברה כדי להעביר קובצי build של Gradle, קובצי מקור של Java ו-Kotlin וקובצי פריסה של XML מ-ExoPlayer 2.19.1 אל AndroidX Media3 1.1.1.

סקירה כללית

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

למה כדאי לעבור ל-Jetpack Media3

  • זהו הבית החדש של ExoPlayer, בעוד ש-com.google.android.exoplayer2 הוצא משימוש.
  • גישה ל-Player API במרכיבים או בתהליכים שונים באמצעות MediaBrowser/MediaController.
  • להשתמש ביכולות המורחבות של ה-API של MediaSession ושל MediaController.
  • אפשר לפרסם יכולות הפעלה באמצעות בקרת גישה פרטנית.
  • מפשטים את האפליקציה על ידי הסרת MediaSessionConnector ו-PlayerNotificationManager.
  • תאימות לאחור לממשקי API של לקוח שתואמים למדיה (MediaBrowserCompat/MediaControllerCompat/MediaMetadataCompat)

ממשקי Media API להעברה אל AndroidX Media3

  • ExoPlayer והתוספים שלו
    הכולל את כל המודולים של פרויקט ExoPlayer הקודם, מלבד המודול mediasession שהופסק. אפשר להעביר אפליקציות או מודולים בהתאם לחבילות ב-com.google.android.exoplayer2 באמצעות סקריפט ההעברה.
  • MediaSessionConnector (בהתאם לחבילות androidx.media.* של androidx.media:media:1.4.3+)
    מסירים את MediaSessionConnector ומשתמשים ב-androidx.media3.session.MediaSession במקום זאת.
  • MediaBrowserServiceCompat (בהתאם לחבילות androidx.media.* של androidx.media:media:1.4.3+)
    מעבירים את תתי-המחלקות של androidx.media.MediaBrowserServiceCompat אל androidx.media3.session.MediaLibraryService, ומעבירים את הקוד שמשתמש ב-MediaBrowserCompat.MediaItem אל androidx.media3.common.MediaItem.
  • MediaBrowserCompat (בהתאם לחבילות android.support.v4.media.* של androidx.media:media:1.4.3+)
    העברת קוד הלקוח באמצעות MediaBrowserCompat או MediaControllerCompat לשימוש ב-androidx.media3.session.MediaBrowser עם androidx.media3.common.MediaItem.

דרישות מוקדמות

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

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

  2. עדכון האפליקציה

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

    • מעלים את compileSdkVersion של האפליקציה ל-32 לפחות.

    • משדרגים את Gradle ואת הפלאגין של Gradle ל-Android Studio לגרסה עדכנית שפועלת עם יחסי התלות המעודכנים שמפורטים למעלה. לדוגמה:

      • גרסה של הפלאגין Android Gradle: 7.1.0
      • גרסת Gradle: 7.4
    • להחליף את כל הצהרות הייבוא עם תווים כלליים לחיפוש שמשתמשות במירכאות (*) ולהשתמש בהצהרות ייבוא תקינות: מוחקים את הצהרות הייבוא עם תווים כלליים לחיפוש ומשתמשים ב-Android Studio כדי לייבא את ההצהרות שמוגדרות במלואן (F2 - Alt/Enter, F2 - Alt/Enter, ...).

    • העברה מ-com.google.android.exoplayer2.PlayerView אל com.google.android.exoplayer2.StyledPlayerView. צריך לעשות זאת כי אין מקביל ל-com.google.android.exoplayer2.PlayerView ב-AndroidX Media3.

העברת ExoPlayer עם תמיכה בסקריפטים

הסקריפט מאפשר מעבר מ-com.google.android.exoplayer2 למבנה החדש של החבילה והמודול ב-androidx.media3. הסקריפט מחיל כמה בדיקות אימות על הפרויקט ומדפיס אזהרות אם האימות נכשל. אחרת, הוא מחיל את המיפויים של הכיתות והחבילות ששינו את השם במשאבים של פרויקט Android gradle שנכתב ב-Java או ב-Kotlin.

usage: ./media3-migration.sh [-p|-c|-d|-v]|[-m|-l [-x <path>] [-f] PROJECT_ROOT]
 PROJECT_ROOT: path to your project root (location of 'gradlew')
 -p: list package mappings and then exit
 -c: list class mappings (precedence over package mappings) and then exit
 -d: list dependency mappings and then exit
 -l: list files that will be considered for rewrite and then exit
 -x: exclude the path from the list of file to be changed: 'app/src/test'
 -m: migrate packages, classes and dependencies to AndroidX Media3
 -f: force the action even when validation fails
 -v: print the exoplayer2/media3 version strings of this script
 -h, --help: show this help text

שימוש בסקריפט ההעברה

  1. מורידים את סקריפט ההעברה מהתג של פרויקט ExoPlayer ב-GitHub, בהתאם לגרסה שאליה עדכנתם את האפליקציה:

    curl -o media3-migration.sh \
      "https://raw.githubusercontent.com/google/ExoPlayer/r2.19.1/media3-migration.sh"
    
  2. יוצרים את קובץ ההפעלה של הסקריפט:

    chmod 744 media3-migration.sh
    
  3. כדי לקבל מידע על האפשרויות, אפשר להריץ את הסקריפט באמצעות --help.

  4. מריצים את הסקריפט עם -l כדי להציג את רשימת הקבצים שנבחרו להעברה (משתמשים ב--f כדי לאלץ את ההצגה ללא אזהרות):

    ./media3-migration.sh -l -f /path/to/gradle/project/root
    
  5. מריצים את הסקריפט באמצעות -m כדי למפות חבילות, כיתות ומודולים ל-Media3. הפעלת הסקריפט עם האפשרות -m תחיל את השינויים על הקבצים שנבחרו.

    • עצירה בשגיאת אימות בלי לבצע שינויים
    ./media3-migration.sh -m /path/to/gradle/project/root
    
    • ביצוע באילוץ

    אם הסקריפט מזהה הפרה של התנאים המוקדמים, אפשר לאלץ את ההעברה באמצעות הדגל -f:

    ./media3-migration.sh -m -f /path/to/gradle/project/root
    
 # list files selected for migration when excluding paths
 ./media3-migration.sh -l -x "app/src/test/" -x "service/" /path/to/project/root
 # migrate the selected files
 ./media3-migration.sh -m -x "app/src/test/" -x "service/" /path/to/project/root

משלימים את השלבים הידניים הבאים אחרי שמריצים את הסקריפט באמצעות האפשרות -m:

  1. בודקים איך הסקריפט שינה את הקוד: משתמשים בכלי diff ומתקנים בעיות אפשריות (כדאי לדווח על באג אם אתם חושבים שיש בסקריפט בעיה כללית שנוצרה מבלי להעביר את האפשרות -f).
  2. יוצרים את הפרויקט: משתמשים ב-./gradlew clean build או ב-Android Studio, בוחרים באפשרות File (קובץ) > Sync Project with Gradle Files (סנכרון הפרויקט עם קובצי Gradle), ואז באפשרות Build (יצירת גרסה build) > Clean project (ניקוי הפרויקט) ואז באפשרות Build (יצירת גרסה build) > Rebuild project (יצירת גרסה build מחדש) (אפשר לעקוב אחרי יצירת הגרסה build בכרטיסייה Build – Build Output (יצירת גרסה build – פלט של יצירת גרסה build) ב-Android Studio).

פעולות שמומלץ לבצע:

  1. פותרים את הבעיה של הסכמה לקבלת שגיאות לגבי שימוש ב-API לא יציב.
  2. החלפת קריאות API שהוצאו משימוש: משתמשים ב-API החלופי שהוצעה. מעבירים את הסמן מעל האזהרה ב-Android Studio ומעיינים ב-JavaDoc של הסמל שהוצא משימוש כדי לברר במה להשתמש במקום בקריאה מסוימת.
  3. מיון של הצהרות הייבוא: פותחים את הפרויקט ב-Android Studio, לוחצים לחיצה ימנית על צומת של תיקיית חבילה בתצוגת הפרויקט ובוחרים באפשרות Optimize imports בחבילות שמכילות את קובצי המקור שהשתנו.

מחליפים את MediaSessionConnector ב-androidx.media3.session.MediaSession

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

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

  2. בקובץ build.gradle שבו שומרים את יחסי התלות, מוסיפים תלות בהטמעה למודול הסשן של AndroidX Media3 ומסירים את התלות הקודמת:

    implementation "androidx.media3:media3-session:1.4.1"
    
  3. מחליפים את הערך MediaSessionCompat ב-androidx.media3.session.MediaSession.

  4. באתר הקוד שבו יצרתם את MediaSessionCompat הקודם, משתמשים ב-androidx.media3.session.MediaSession.Builder כדי ליצור MediaSession. מעבירים את הנגן כדי ליצור את הכלי ליצירת סשנים.

    val player = ExoPlayer.Builder(context).build()
    mediaSession = MediaSession.Builder(context, player)
        .setSessionCallback(MySessionCallback())
        .build()
    
  5. צריך להטמיע את MySessionCallback לפי הדרישות של האפליקציה. זה אופציונלי. אם רוצים לאפשר לבקרים להוסיף פריטי מדיה לנגן, צריך להטמיע את MediaSession.Callback.onAddMediaItems(). היא משתמשת במגוון שיטות API, קיימות וקודמות, שמוסיפות פריטי מדיה לנגן כדי להפעיל אותם באופן שתואם לאחור. זה כולל את השיטות MediaController.set/addMediaItems() של הבקר Media3, וגם את השיטות TransportControls.prepareFrom*/playFrom* של ה-API הקודם. הטמעה לדוגמה של onAddMediaItems מופיעה ב-PlaybackService של אפליקציית הדגמה של הסשן.

  6. משחררים את סשן המדיה באתר הקוד שבו השמדתם את הסשן לפני ההעברה:

    mediaSession?.run {
      player.release()
      release()
      mediaSession = null
    }
    

הפונקציונליות של MediaSessionConnector ב-Media3

בטבלה הבאה מפורטים ממשקי ה-API של Media3 שמטפלים בפונקציות שהוטמעו בעבר ב-MediaSessionConnector.

MediaSessionConnectorAndroidX Media3
CustomActionProvider MediaSession.Callback.onCustomCommand()/ MediaSession.setCustomLayout()
PlaybackPreparer MediaSession.Callback.onAddMediaItems() (הקריאה ל-prepare() מתבצעת באופן פנימי)
QueueNavigator ForwardingPlayer
QueueEditor MediaSession.Callback.onAddMediaItems()
RatingCallback MediaSession.Callback.onSetRating()
PlayerNotificationManager DefaultMediaNotificationProvider/ MediaNotification.Provider

העברה של MediaBrowserService אל MediaLibraryService

AndroidX Media3 כולל את MediaLibraryService שמחליף את MediaBrowserServiceCompat. ב-JavaDoc של MediaLibraryService ובסופר-קלאס שלו MediaSessionService יש מבוא טוב ל-API ולמודל התכנות האסינכרוני של השירות.

ה-MediaLibraryService תואם לאחור ל-MediaBrowserService. אפליקציית לקוח שמשתמשת ב-MediaBrowserCompat או ב-MediaControllerCompat ממשיכה לפעול ללא שינויים בקוד כשמתחברים ל-MediaLibraryService. מבחינת הלקוח, לא ברור אם האפליקציה משתמשת ב-MediaLibraryService או ב-MediaBrowserServiceCompat מדור קודם.

תרשים של רכיבי האפליקציה עם שירות, פעילות ואפליקציות חיצוניות.
איור 1: סקירה כללית של הרכיבים של אפליקציית המדיה
  1. כדי שתואם לאחור יפעל, צריך לרשום את שני ממשקי השירות בשירות ב-AndroidManifest.xml. כך הלקוח יכול למצוא את השירות שלכם לפי ממשק השירות הנדרש:

    <service android:name=".MusicService" android:exported="true">
        <intent-filter>
            <action android:name="androidx.media3.session.MediaLibraryService"/>
            <action android:name="android.media.browse.MediaBrowserService" />
        </intent-filter>
    </service>
    
  2. בקובץ build.gradle שבו אתם שומרים את יחסי התלות, מוסיפים תלות הטמעה למודול הסשן של AndroidX Media3 ומסירים את תלות המורשת:

    implementation "androidx.media3:media3-session:1.4.1"
    
  3. משנים את השירות כך שיירש מ-MediaLibraryService במקום מ-MediaBrowserService. כפי שצוין קודם, ה-MediaLibraryService תואם ל-MediaBrowserService הקודם. לכן, ממשק ה-API הרחב יותר שהשירות מציע ללקוחות עדיין זהה. לכן סביר להניח שאפליקציה יכולה לשמור על רוב הלוגיקה שנדרשת להטמעת MediaBrowserService ולהתאים אותה ל-MediaLibraryService החדש.

    ההבדלים העיקריים בהשוואה ל-MediaBrowserServiceCompat מדור קודם:

    • הטמעת השיטות של מחזור החיים של השירות: השיטות שצריך לשנות בשירות עצמו הן onCreate/onDestroy, שבהן האפליקציה מקצה או משחררת את הסשן של הספרייה, את הנגן ומשאבים אחרים. בנוסף לשיטות הסטנדרטיות של מחזור החיים של שירות, האפליקציה צריכה לבטל את onGetSession(MediaSession.ControllerInfo) כדי להחזיר את MediaLibrarySession שנוצר ב-onCreate.

    • הטמעת MediaLibraryService.MediaLibrarySessionCallback: כדי ליצור סשן, צריך MediaLibraryService.MediaLibrarySessionCallback שמטמיע את שיטות ה-API בפועל של הדומיין. לכן, במקום לשנות את שיטות ה-API של השירות הקודם, אתם יכולים לשנות את השיטות של MediaLibrarySession.Callback.

      לאחר מכן הקריאה החוזרת משמשת ליצירת MediaLibrarySession:

      mediaLibrarySession =
            MediaLibrarySession.Builder(this, player, MySessionCallback())
               .build()
      

      ה-API המלא של MediaLibrarySessionCallback מופיע במסמכי התיעוד של ה-API.

    • הטמעה של MediaSession.Callback.onAddMediaItems(): פונקציית ה-callback‏ onAddMediaItems(MediaSession, ControllerInfo, List<MediaItem>) משמשת לשיטות API שונות, נוכחיות וקודמות, שמוסיפות פריטים של מדיה לנגן להפעלה באופן תואם לאחור. זה כולל את השיטות MediaController.set/addMediaItems() של בקר Media3, וגם את השיטות TransportControls.prepareFrom*/playFrom* של ה-API הקודם. דוגמה להטמעה של קריאה חוזרת מפורטת ב-PlaybackService של אפליקציית הדגמה של הסשן.

    • ב-AndroidX Media3 נעשה שימוש ב-androidx.media3.common.MediaItem במקום ב-MediaBrowserCompat.MediaItem וב-MediaMetadataCompat. חלקים מהקוד שמקושרים לכיתות הקודמות צריכים להשתנות בהתאם, או למפות ל-Media3 MediaItem במקום זאת.

    • מודל התכנות האסינכרוני הכללי השתנה ל-Futures, בניגוד לגישה הנפרדת של Result ב-MediaBrowserServiceCompat. הטמעת השירות יכולה להחזיר ListenableFuture אסינכרוני במקום לנתק תוצאה או להחזיר Future מיידי כדי להחזיר ערך ישירות.

הסרת PlayerNotificationManager

MediaLibraryService תומך בהתראות מדיה באופן אוטומטי, ואפשר להסיר את PlayerNotificationManager כשמשתמשים ב-MediaLibraryService או ב-MediaSessionService.

אפליקציה יכולה להתאים אישית את ההתראה על ידי הגדרת MediaNotification.Provider בהתאמה אישית ב-onCreate() שמחליף את DefaultMediaNotificationProvider. לאחר מכן, MediaLibraryService יטפל בהפעלת השירות בחזית, לפי הצורך.

כשמבטלים את MediaLibraryService.updateNotification(), האפליקציה יכולה לקבל בעלות מלאה על פרסום התראה ולהתחיל/להפסיק את השירות בחזית, לפי הצורך.

העברת קוד לקוח באמצעות MediaBrowser

ב-AndroidX Media3, MediaBrowser מטמיע את הממשקים MediaController/Player, וניתן להשתמש בו כדי לשלוט בהפעלת המדיה בנוסף לגלישה בספריית המדיה. אם נאלצתם ליצור MediaBrowserCompat ו-MediaControllerCompat בעולם הקודם, תוכלו לעשות את אותו הדבר באמצעות שימוש רק ב-MediaBrowser ב-Media3.

אפשר ליצור MediaBrowser ולהמתין לחיבור לשירות:

scope.launch {
    val sessionToken =
        SessionToken(context, ComponentName(context, MusicService::class.java)
    browser =
        MediaBrowser.Builder(context, sessionToken))
            .setListener(BrowserListener())
            .buildAsync()
            .await()
    // Get the library root to start browsing the library.
    root = browser.getLibraryRoot(/* params= */ null).await();
    // Add a MediaController.Listener to listen to player state events.
    browser.addListener(playerListener)
    playerView.setPlayer(browser)
}

במאמר שליטה בהפעלה בסשן המדיה מוסבר איך יוצרים MediaController כדי לשלוט בהפעלה ברקע.

שלבים נוספים ופינוי מקום

שגיאות API לא יציבות

אחרי המעבר ל-Media3, יכול להיות שיופיעו שגיאות איתור שגיאות בקוד לגבי שימוש לא יציב ב-API. השימוש בממשקי ה-API האלה בטוח, ושגיאות ה-lint הן תוצר לוואי של ההתחייבויות החדשות שלנו לתאימות בינארית. אם לא נדרשת תאימות בינארית מחמירה, אפשר להסתיר את השגיאות האלה באופן בטוח באמצעות ההערה @OptIn.

רקע

לא בגרסת ExoPlayer v1 ולא בגרסת ExoPlayer v2 הייתה אחריות קפדנית על תאימות הבינארית של הספרייה בין גרסאות עוקבות. ממשק ה-API של ExoPlayer גדול מאוד מעצם הגדרתו, כדי לאפשר לאפליקציות להתאים אישית כמעט כל היבט של ההפעלה. בגרסאות הבאות של ExoPlayer יתבצעו מדי פעם שינויים בשמות של סמלים או שינויים אחרים שמשביתים את הקוד (למשל, שיטות נדרשות חדשות בממשקים). ברוב המקרים, כדי לאפשר למפתחים להעביר את השימוש שלהם, הבעיות האלה טופלו על ידי הצגת הסמל החדש לצד הוצאה משימוש של הסמל הישן בגרסאות מסוימות, אבל לא תמיד היה אפשר לעשות זאת.

השינויים האלה גרמו לשתי בעיות אצל משתמשי הספריות של ExoPlayer בגרסה 1 ובגרסה 2:

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

שיפורים ב-Media3

Media3 מבטיחה תאימות בינארית לקבוצת משנה של ממשק ה-API. החלקים שלא מובטחת להם תאימות בינארית מסומנים ב-@UnstableApi. כדי להבהיר את ההבדל הזה, שימוש בסמלי API לא יציבים יגרום לשגיאת איתור שגיאות בקוד (lint) אלא אם הם יסומנו ב-@OptIn.

אחרי המעבר מ-ExoPlayer v2 ל-Media3, יכול להיות שתראו הרבה שגיאות לא יציבות של איתור שגיאות בקוד ב-API. לכן, יכול להיות שייראה ש-Media3 'פחות יציב' מ-ExoPlayer v2. זה לא נכון. לחלקים ה'לא יציבים' ב-Media3 API יש את אותה רמת יציבות כמו כל שטח ה-API של ExoPlayer v2, והאחריות של הפלטפורמה היציבה של Media3 API לא זמינה ב-ExoPlayer v2 בכלל. ההבדל הוא פשוט: עכשיו תקבלו התראות על רמות היציבות השונות כשיתגלה שגיאה באיתור שגיאות בקוד.

טיפול בשגיאות לא יציבות של איתור שגיאות בקוד ב-API

הסבר איך להוסיף הערות לשימוש ב-Java וב-Kotlin בממשקי API לא יציבים באמצעות @OptIn זמין בקטע פתרון בעיות שקשורות לשגיאות השגיאות בקוד.

ממשקי API שהוצאו משימוש

יכול להיות שתבחינו בקריאות לממשקי API שהוצאו משימוש ב-Android Studio. מומלץ להחליף את הקריאות האלה באפשרות החלופית המתאימה. מעבירים את העכבר מעל הסמל כדי לראות את ה-JavaDoc שמציין באיזה API להשתמש במקום זאת.

צילום מסך: איך להציג JavaDoc עם חלופה לשיטה שהוצאה משימוש
איור 3: בהסבר הקצר על JavaDoc ב-Android Studio מופיעה הצעה חלופית לכל סמל שהוצא משימוש.

דוגמאות קוד ואפליקציות הדגמה

  • אפליקציית הדגמה של סשן AndroidX Media3 (לנייד ול-WearOS)
    • פעולות בהתאמה אישית
    • התראה בממשק המשתמש של המערכת, MediaButton/BT
    • שליטה בהפעלה של Google Assistant
  • UAMP: Android Media Player (branch media3) (לנייד, AutomotiveOS)
    • התראה בממשק המשתמש של המערכת, MediaButton/BT, המשך הפעלה
    • בקרת ההפעלה של Google Assistant או WearOS
    • AutomotiveOS: פקודה והרשמה בהתאמה אישית