מדריך להעברת נתונים מ-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.
  • להשתמש ביכולות המתקדמות של MediaSession ו-MediaController API.
  • הצגת יכולות ההפעלה של המודעות באמצעות בקרת גישה פרטנית.
  • פשטו את האפליקציה על ידי הסרת MediaSessionConnector ו-PlayerNotificationManager.
  • תואם לאחור לממשקי API של לקוחות media-compat (MediaBrowserCompat/MediaControllerCompat/MediaMetadataCompat)

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

MediaSessionConnector‫AndroidX Media3
CustomActionProvider MediaSession.Callback.onCustomCommand()/ MediaSession.setMediaButtonPreferences()
PlaybackPreparer MediaSession.Callback.onAddMediaItems() (prepare() הוא השם הפנימי)
QueueNavigator ForwardingSimpleBasePlayer
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 שבו מנהלים את התלויות, מוסיפים תלות implementation במודול הסשן של AndroidX Media3 ומסירים את התלות מדור קודם:

    implementation "androidx.media3:media3-session:1.7.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, יכול להיות שיוצגו שגיאות lint לגבי שימושים לא יציבים ב-API. ממשקי ה-API האלה בטוחים לשימוש, והשגיאות של lint הן תוצר לוואי של הערבויות החדשות שלנו לתאימות בינארית. אם לא נדרשת תאימות בינארית מחמירה, אפשר להשבית את השגיאות האלה בבטחה באמצעות הערה @OptIn.

רקע

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

השינויים האלה שגרמו לבעיות הובילו לשתי בעיות למשתמשים בספריות ExoPlayer v1 ו-v2:

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

שיפורים ב-Media3

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

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

טיפול בשגיאות לא יציבות של API lint

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

ממשקי 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: פקודה מותאמת אישית וכניסה