راهنمای مهاجرت AndroidX Media3

برنامه‌هایی که در حال حاضر از کتابخانه مستقل com.google.android.exoplayer2 و androidx.media استفاده می‌کنند باید به androidx.media3 مهاجرت کنند. از اسکریپت مهاجرت برای انتقال فایل های ساخت gradle، فایل های منبع جاوا و Kotlin و فایل های طرح بندی XML از ExoPlayer 2.19.1 به AndroidX Media3 1.1.1 استفاده کنید.

نمای کلی

قبل از مهاجرت، بخش‌های زیر را مرور کنید تا در مورد مزایای APIهای جدید، APIهای انتقال و پیش نیازهایی که پروژه برنامه شما باید رعایت کند بیشتر بدانید.

چرا به Jetpack Media3 مهاجرت کنید

  • این خانه جدید ExoPlayer است، در حالی که com.google.android.exoplayer2 متوقف شده است.
  • با MediaBrowser / MediaController به API Player در بین اجزا/فرآیندها دسترسی داشته باشید.
  • از قابلیت های توسعه یافته MediaSession و MediaController API استفاده کنید.
  • قابلیت های پخش را با کنترل دسترسی دقیق تبلیغ کنید.
  • برنامه خود را با حذف MediaSessionConnector و PlayerNotificationManager ساده کنید.
  • سازگار با APIهای کلاینت با رسانه سازگار ( MediaBrowserCompat / MediaControllerCompat / MediaMetadataCompat )

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+ )
    برای استفاده از androidx.media3.session.MediaBrowser با androidx.media3.common.MediaItem ، کد سرویس گیرنده را با استفاده از MediaBrowserCompat یا MediaControllerCompat منتقل کنید.

پیش نیازها

  1. مطمئن شوید که پروژه شما تحت کنترل منبع است

    اطمینان حاصل کنید که می توانید به راحتی تغییرات اعمال شده توسط ابزارهای انتقال اسکریپت را برگردانید. اگر هنوز پروژه خود را تحت کنترل منبع ندارید، اکنون زمان خوبی برای شروع آن است. اگر به دلایلی نمی‌خواهید این کار را انجام دهید، قبل از شروع مهاجرت، یک نسخه پشتیبان از پروژه خود تهیه کنید.

  2. برنامه خود را به روز کنید

    • توصیه می‌کنیم پروژه خود را به‌روزرسانی کنید تا از جدیدترین نسخه کتابخانه ExoPlayer استفاده کنید و هرگونه تماس با روش‌های منسوخ را حذف کنید. اگر می‌خواهید از اسکریپت برای انتقال استفاده کنید ، باید نسخه‌ای را که به‌روزرسانی می‌کنید با نسخه‌ای که توسط اسکریپت مدیریت می‌شود مطابقت دهید.

    • کامپایلSdkVersion برنامه خود را به حداقل 32 افزایش دهید.

    • Gradle و افزونه Android Studio Gradle را به نسخه اخیر ارتقا دهید که با وابستگی های به روز شده از بالا کار می کند. به عنوان مثال:

      • نسخه پلاگین اندروید 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 را تسهیل می کند. این اسکریپت برخی از بررسی‌های اعتبارسنجی را روی پروژه شما اعمال می‌کند و در صورت عدم موفقیت اعتبار، هشدارها را چاپ می‌کند. در غیر این صورت، نگاشت کلاس ها و بسته های تغییر نام یافته را در منابع پروژه gradle اندروید نوشته شده در جاوا یا کاتلین اعمال می کند.

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 استفاده کنید یا در اندروید استودیو File > Sync Project with Gradle Files ، سپس Build > Clean project و سپس Build > Rebuild را انتخاب کنید (بیلد خود را در تب «Build - Build Output» زیر نظر بگیرید. اندروید استودیو .

مراحل پیشنهادی پیگیری:

  1. خطاهای مربوط به استفاده از APIهای ناپایدار را حل کنید.
  2. جایگزینی تماس‌های API منسوخ شده : از API جایگزین پیشنهادی استفاده کنید. نشانگر را روی اخطار در Android Studio نگه دارید و با JavaDoc نماد منسوخ شده مشورت کنید تا دریابید که به جای تماس معین از چه چیزی استفاده کنید.
  3. مرتب سازی عبارت های import : پروژه را در Android Studio باز کنید، سپس روی گره پوشه بسته در نمایشگر پروژه کلیک راست کرده و Optimize imports on بسته هایی که حاوی فایل های منبع تغییر یافته هستند را انتخاب کنید.

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.5.0"
    
  3. MediaSessionCompat را با androidx.media3.session.MediaSession جایگزین کنید.

  4. در سایت کدی که MediaSessionCompat قدیمی را ایجاد کرده‌اید، از androidx.media3.session.MediaSession.Builder برای ساخت MediaSession استفاده کنید. برای ساخت Session Builder از بازیکن عبور دهید .

    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.setCustomLayout()
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 که در آن وابستگی های خود را حفظ می کنید، یک وابستگی پیاده سازی را به ماژول جلسه AndroidX Media3 اضافه کنید و وابستگی قدیمی را حذف کنید:

    implementation "androidx.media3:media3-session:1.5.0"
    
  3. همانطور که قبلاً گفته شد MediaLibraryService MediaLibraryService MediaBrowserService MediaBrowserService سازگار است. بر این اساس، API گسترده‌تری که این سرویس به مشتریان ارائه می‌کند، همچنان یکسان است. بنابراین این احتمال وجود دارد که یک برنامه بتواند بیشتر منطق مورد نیاز برای اجرای MediaBrowserService را حفظ کند و آن را برای MediaLibraryService جدید تطبیق دهد.

    تفاوت های اصلی در مقایسه با MediaBrowserServiceCompat قدیمی به شرح زیر است:

    • روش‌های چرخه عمر سرویس را پیاده‌سازی کنید: روش‌هایی که باید روی خود سرویس نادیده گرفته شوند، onCreate/onDestroy هستند، جایی که یک برنامه جلسه کتابخانه، پخش‌کننده و سایر منابع را اختصاص/آزاد می‌کند. علاوه بر روش‌های استاندارد چرخه عمر سرویس، یک برنامه باید onGetSession(MediaSession.ControllerInfo) لغو کند تا MediaLibrarySession ساخته شده در onCreate را برگرداند.

    • Implement MediaLibraryService.MediaLibrarySessionCallback: ایجاد یک جلسه به MediaLibraryService.MediaLibrarySessionCallback نیاز دارد که متدهای API دامنه واقعی را پیاده سازی می کند. بنابراین به جای نادیده گرفتن متدهای API سرویس قدیمی، روش های MediaLibrarySession.Callback را لغو می کنید.

      سپس از callback برای ساخت MediaLibrarySession استفاده می شود:

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

      API کامل MediaLibrarySessionCallback را در اسناد API پیدا کنید.

    • Implement MediaSession.Callback.onAddMediaItems() : پاسخ به تماس onAddMediaItems(MediaSession, ControllerInfo, List<MediaItem>) روش‌های مختلف API فعلی و قدیمی را ارائه می‌کند که آیتم‌های رسانه‌ای را به پخش کننده برای پخش به روشی سازگار با عقب اضافه می‌کند. این شامل متدهای MediaController.set/addMediaItems() کنترلر Media3 و همچنین متدهای TransportControls.prepareFrom*/playFrom* در API قدیمی است. نمونه‌ای از اجرای callback را می‌توانید در PlaybackService برنامه نمایشی جلسه پیدا کنید.

    • AndroidX Media3 به جای MediaBrowserCompat.MediaItem و MediaMetadataCompat از androidx.media3.common.MediaItem استفاده می کند. بخش‌هایی از کد شما که به کلاس‌های قدیمی مرتبط است باید بر اساس آن تغییر یابند یا به جای آن به Media3 MediaItem نگاشت شوند.

    • مدل برنامه‌نویسی Futures عمومی برخلاف رویکرد Result قابل جداسازی MediaBrowserServiceCompat به Futures تغییر کرد. پیاده‌سازی سرویس شما می‌تواند به جای جدا کردن یک نتیجه، یک 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 برای کنترل پخش در پس‌زمینه، به Control playback در جلسه رسانه نگاهی بیندازید.

مراحل بعدی و پاکسازی

خطاهای ناپایدار API

پس از مهاجرت به Media3، ممکن است خطاهای پرزهایی در مورد استفاده های ناپایدار API مشاهده کنید. استفاده از این API ها ایمن است و خطاهای پرز محصول جانبی تضمین های سازگاری باینری جدید ما هستند. اگر به سازگاری دقیق باینری نیاز ندارید، این خطاها را می توان با یک حاشیه نویسی @OptIn با خیال راحت سرکوب کرد.

پس زمینه

هیچ کدام ExoPlayer v1 یا v2 تضمین های دقیقی در مورد سازگاری باینری کتابخانه بین نسخه های بعدی ارائه نکردند. سطح API ExoPlayer از نظر طراحی بسیار بزرگ است تا به برنامه ها اجازه دهد تقریباً هر جنبه ای از پخش را سفارشی کنند. نسخه‌های بعدی ExoPlayer گاهی اوقات تغییر نام نمادها یا سایر تغییرات قطعی را معرفی می‌کنند (به عنوان مثال روش‌های جدید مورد نیاز در رابط‌ها). در بیشتر موارد، این شکستگی‌ها با معرفی نماد جدید در کنار حذف نماد قدیمی برای چند نسخه کاهش می‌یابد تا به توسعه‌دهندگان فرصت داده شود تا کاربردهای خود را تغییر دهند، اما این همیشه ممکن نبود.

این تغییرات شکسته منجر به دو مشکل برای کاربران کتابخانه‌های ExoPlayer v1 و v2 شد:

  1. ارتقا از نسخه ExoPlayer می تواند باعث توقف کامپایل کد شود.
  2. برنامه‌ای که هم مستقیماً و هم از طریق یک کتابخانه میانی به ExoPlayer وابسته بود باید اطمینان حاصل می‌کرد که هر دو وابستگی یک نسخه هستند، در غیر این صورت ناسازگاری‌های باینری می‌تواند منجر به خرابی زمان اجرا شود.

بهبود در Media3

Media3 سازگاری باینری را برای زیر مجموعه ای از سطح API تضمین می کند. قسمت هایی که سازگاری باینری را تضمین نمی کنند با @UnstableApi علامت گذاری شده اند. به منظور روشن کردن این تمایز، استفاده از نمادهای API ناپایدار یک خطای پرز ایجاد می کند، مگر اینکه با @OptIn حاشیه نویسی شده باشند.

پس از مهاجرت از ExoPlayer v2 به Media3، ممکن است خطاهای پرز ناپایدار API زیادی را مشاهده کنید. این ممکن است به نظر برسد که Media3 نسبت به ExoPlayer v2 «پایدارتر» است. اینطور نیست. بخش‌های «ناپایدار» Media3 API همان سطح پایداری کل سطح API ExoPlayer v2 را دارند و تضمین‌های سطح پایدار Media3 API اصلاً در ExoPlayer نسخه 2 موجود نیست. تفاوت در این است که اکنون یک خطای پرز به شما در مورد سطوح مختلف پایداری هشدار می دهد.

خطاهای پرز ناپایدار API را مدیریت کنید

برای جزئیات نحوه حاشیه نویسی استفاده از جاوا و کاتلین از APIهای ناپایدار با @OptIn به بخش عیب یابی در این خطاهای پرز مراجعه کنید.

API های منسوخ شده

ممکن است متوجه شوید که تماس‌ها با APIهای منسوخ شده در Android Studio انجام می‌شود. توصیه می کنیم چنین تماس هایی را با جایگزین مناسب جایگزین کنید. ماوس را روی نماد نگه دارید تا JavaDoc را ببینید که به شما می گوید از کدام API استفاده کنید.

اسکرین شات: نحوه نمایش JavaDoc با روش جایگزین منسوخ
شکل 3 : راهنمای ابزار JavaDoc در Android Studio یک جایگزین برای هر نماد منسوخ شده پیشنهاد می کند.

نمونه کد و برنامه های آزمایشی

  • برنامه نمایشی جلسه AndroidX Media3 (موبایل و WearOS)
    • اقدامات سفارشی
    • اعلان رابط کاربری سیستم، MediaButton/BT
    • کنترل پخش Google Assistant
  • UAMP: Android Media Player (شاخه media3) (موبایل، AutomotiveOS)
    • اعلان رابط کاربری سیستم، MediaButton/BT، ازسرگیری پخش
    • کنترل پخش Google Assistant/WearOS
    • AutomotiveOS: فرمان سفارشی و ورود به سیستم